В WPF: Children.Remove или Children.Clear не освобождает объекты - PullRequest
12 голосов
/ 30 марта 2010

Обновление : я пробовал это на другом, более чистом компьютере.Я не мог воспроизвести это на этой машине.Если я выясню, какой вызывающий компонент (VSStudio) вызывает это, я сообщу вам.

Я создаю некоторые элементы UIE из кода и ожидаю, что сборщик мусора прояснит ситуацию.Тем не менее, объекты не свободны в то время, когда я ожидал этого.Я ожидал, что они будут освобождены в RemoveAt (0), но они будут освобождены только в конце программы.

Как я могу сделать объекты освобожденными при удалении из коллекции Children на Canvas?

<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");
  }
}

Ответы [ 5 ]

8 голосов
/ 30 марта 2010

Изменение

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. Это не слишком сложно для перенастройки (и для большинства вещей это лучшее решение.)

3 голосов
/ 02 июня 2014

Вы можете найти это интересным. Недавно я обнаружил, что расширение разметки x: Name хранит ссылку на UIElement в родительском элементе управления в словаре с ключевым словом.

Когда вы удаляете UIElement из его родителя, словарь сохраняет ссылку на элемент управления.

Здесь есть запись в блоге / отладка утечки памяти здесь: WPF x: имя утечки памяти

Решение состоит в том, чтобы не использовать x: Name или гарантировать, что элементы управления, которые поддерживаются x: Name, очищаются, чтобы не занимать слишком много памяти до сбора части визуального дерева.

Обновление: Вы отменили регистрацию именованного класса, используя NameScope

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! 
3 голосов
/ 30 марта 2010

В C # существует 3 поколения сборки мусора, поэтому даже если нет ссылок на ваши объекты, для их освобождения может потребоваться 3 сборки мусора.

Вы можете использовать параметр GC.Collect () для принудительной сборки мусора 3-го поколения,
однако лучше всего не вызывать GC.Collect () самостоятельно,
вместо этого используйте интерфейс IDisposable и привяжите Children к ObservableCollection и когда вы получаете событие CollectionChanged Dispose () всех удаленных объектов.

3 голосов
/ 30 марта 2010

Объекты в C # автоматически не «освобождаются», как только они больше не используются.

Скорее, когда вы удаляете объект из элемента управления, он становится пригодным для сбора мусора в этот момент, при условии, что у вас нет других ссылок на этот UIelement.

Как только объект «неуткорен» (нет прямых или косвенных ссылок ни на один из используемых объектов в вашем приложении), он становится пригодным для сбора. Затем сборщик мусора, в конце концов, очистит ваш объект, но когда это происходит, вы не можете (как правило) контролировать его.

Просто верь, что это в конечном итоге будет очищено. В этом прелесть C # (и .NET в целом) - управление этим процессом и его беспокойство для вас.


Редактировать: после некоторого тестирования кажется, что Window содержит ссылку на UIelement до следующего прохода макета. Вы можете заставить это произойти, добавив вызов к:

this.UpdateLayout();

После удаления элемента (ов) с холста Children. Это сделает объекты доступными для ГХ.

0 голосов
/ 30 марта 2010

У нас была такая же проблема, и мы подумали, что это может быть причиной. Но мы проанализировали наш проект с использованием инструментов профилирования памяти и обнаружили, что это не имеет ничего общего с Grid.Remove или Grid.RemoveAt. Поэтому я думаю, что мое предложение - просто взглянуть на ваш проект в инструменте профилирования памяти и посмотреть, что происходит внутри вашего проекта. надеется, что это поможет.

...