В WPF:Children.Remove или Children.Clear не освобождают объекты.
-
23-09-2019 - |
Вопрос
Обновлять:Я попробовал это на другой, более чисто установленной машине.Я не смог воспроизвести это на этой машине.Если я выясню, какой компонент (VSStudio) вызывает это, я сообщу вам.
Я создал несколько UIElements из кода и ожидал, что сбор мусора прояснит ситуацию.Однако объекты не были освобождены в тот момент, когда я этого ожидал.Я ожидал, что они будут освобождены при RemoveAt(0), но они освобождаются только в конце программы.
Как я могу освободить объекты при их удалении из коллекции Children на холсте?
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
MouseDown="Window_MouseDown">
<Grid>
<Canvas x:Name="main" />
</Grid>
</Window>
Код:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
GC.Collect(); // This should pick up the control removed at a previous MouseDown
GC.WaitForPendingFinalizers(); // Doesn't help either
if (main.Children.Count == 0)
main.Children.Add(new MyControl() { Background = Brushes.Yellow, Width = 100, Height = 50 });
else
main.Children.RemoveAt(0);
}
}
public class MyControl : UserControl
{
~MyControl()
{
Debug.WriteLine("Goodbye");
}
}
Нет правильного решения
Другие советы
Изменять
public class MyControl : UserControl
к
public class MyControl : ContentControl
и он попрощается (после второго удаления элемента управления). Я также проверил, что память не протекает, используя
Debug.WriteLine("mem: " + GC.GetTotalMemory(true).ToString());
Также см этот:
Вы удаляете TestControl, очищая Grid.Children, но он не сразу подлежит сборке мусора.Ожидается несколько асинхронных операций над ним, и его нельзя выполнить GC до завершения этих операций (к ним относятся создание события Unloaded и некоторый код очистки в механизме рендеринга).
Я проверил, что если вы дождетесь завершения этих операций (скажем, запланировав операцию Dispatcher с приоритетом ContextIdle), TestControl станет доступным для GC, независимо от наличия привязки к TextBlock.
UserControl либо должен иметь внутреннее событие, которое не может быть быстро устранено, либо это может быть ошибка в VS2010 RC.Я бы сообщил об этом через Connect, но сейчас переключусь на ContentControl.
Поскольку вы используете UserControl, я предполагаю, что вам также придется переключиться на использование шаблона Generic.xaml.Это не слишком сложное изменение (и для большинства вещей это лучшее решение).
Объекты в C# не «освобождаются» автоматически, как только они больше не используются.
Скорее, когда вы удаляете объект из своего элемента управления, он становится право на вывоз мусора в этот момент, если у вас нет других ссылок на этот элемент UIelement.
Как только объект «отключен» (нет прямых или косвенных ссылок на любой используемый объект в вашем приложении), он становится пригодным для сбора.Сборщик мусора, в конце концов, очистит ваш объект, но когда это происходит, вы (обычно) не можете это контролировать.
Просто верьте, что со временем все будет очищено.В этом прелесть C# (и .NET в целом) — управление и заботы об этом выполняются за вас.
Редактировать:После некоторого тестирования выяснилось, что Window хранит ссылку на UIelement до следующего прохода макета.Вы можете заставить это произойти, добавив вызов:
this.UpdateLayout();
После удаления элемента(ов) с холста Children.Это сделает объекты доступными для GC.
В C# существует три поколения сборки мусора, поэтому, даже если на ваши объекты нет ссылок, для их освобождения может потребоваться три сборки мусора.
Вы можете использовать параметр GC.Collect() для принудительной сборки мусора 3-го поколения,
однако лучший подход — не вызывать GC.Collect() самостоятельно,
Вместо этого используйте интерфейс Idisposable и свяжите детей с ObservableCollection, и когда вы получаете соревнование по сбору сборов Dispose () любых удаленных объектов.
Возможно, вам это будет интересно.Недавно я узнал, что расширение разметки x:Name хранит ссылку на UIElement в родительском элементе управления в словаре, связанном с именем строки.
Когда вы удаляете UIElement из его родителя, словарь сохраняет ссылку на элемент управления.
Здесь есть сообщение в блоге/видео об отладке утечки памяти: WPF X: утечка памяти имени
Решение состоит в том, чтобы не использовать x:Name или обеспечить удаление элементов управления, которые поддерживаются x:Name, чтобы не занимать слишком много памяти перед сбором раздела визуального дерева.
Обновлять: Вы отменяете регистрацию именованного класса, используя Область имен
this.grid.Children.Remove(child); // Remove the child from visual tree
NameScope.GetNameScope(this).UnregisterName("child"); // remove the keyed name
this.child = null; // null the field
// Finally it is free to be collected!
У нас была такая же проблема и мы тоже подумали, что это может быть причиной.Но мы проанализировали наш проект с помощью инструментов профилирования памяти и обнаружили, что здесь нет ничего общего с Grid.Remove или Grid.RemoveAt.Поэтому я думаю, что я предлагаю просто взглянуть на ваш проект с помощью инструмента профилирования памяти и посмотреть, что происходит внутри вашего проекта.надеется, что это поможет.