Добавление и удаление элементов управления и использования памяти в WindowsForm - PullRequest
2 голосов
/ 09 января 2010

У меня есть WindowsForm с панелью управления, которую я использую для отображения пользовательских элементов управления. Я добавляю элементы управления таким образом:

private void AddControl(Control control)
{
    panel.Controls.Clear();
    control.Size = new Size(panel.Width - 1, panel.Height - 1);
    control.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles. Top;
    panel.Controls.Add(control);
}

..

AddControl(new ucSomeControl());

Я просто нажал на каждую кнопку, которая использует AddControl (), и увидел, что использование памяти увеличивается с каждым разом. Я оставил приложение запущенным, ничего не делая, в течение полутора часов, и использование памяти было уменьшено с 140 МБ до 138 МБ, как 2 МБ. Как вы думаете, это нормально, или я делаю что-то не так с моим методом добавления элементов управления, который я должен / мог бы улучшить для меньшего использования памяти?

Followup

Я создал 4 версии своего приложения: Debug, Release, with Dispose, с вызовом manuel GC.

С моим исходным кодом

Существует небольшая разница между отладочной и выпускной версиями моего приложения, когда речь идет об использовании памяти, например, 5 МБ. Проблема с этими версиями заключается в том, что чем больше я нажимаю на кнопки, даже если я нажимаю ту же кнопку и снова создаю тот же UserControl, использование памяти также увеличивается.

С утилизировать

Я добавил код Криса Арнольда Dispose. Использование памяти значительно ниже, и хотя создание большего и большего количества элементов управления все еще увеличивает использование памяти, теперь каждый элемент управления использует намного меньше памяти. Это было достойное дополнение.

С вызовом manuel GC

Я добавил этот код после Dispose:

GC.Collect();
GC.WaitForPendingFinalizers();

Бинго! Даже меньше использования памяти, чем утилизировать код. Самое приятное то, что даже если я создаю новые элементы управления снова и снова, увеличение использования памяти очень мало, почти тривиально.

Мне очень понравилось использовать метод Dispose + GC, но каждая отдельная статья, которую я написал о вызовах manuel GC, категорически не одобряет его использование. Даже если у меня нет пользовательских финализаторов / деструкторов, я не решил использовать их или нет ..

Ответы [ 3 ]

5 голосов
/ 09 января 2010

Вы можете увидеть, что происходит, с помощью TaskMgr.exe, вкладка Процессы. Вид + Выберите Столбцы и отметьте «Объекты ПОЛЬЗОВАТЕЛЯ». Этот столбец отслеживает дескрипторы окон. Обратите внимание, что значение этого столбца продолжает увеличиваться при нажатии кнопки. Вызов GC.Collect () не приводит к падению. Ваша программа вылетит и сгорит, когда достигнет 10000.

Класс Control является единственным классом в .NET Framework. Я знаю, что требует вызова Dispose (). Финализатора недостаточно для того, чтобы освободить дескриптор окна. Вызов Dispose () обычно полностью автоматический, родительский элемент управления делает это, когда он удаляется. Конечным родителем является объект Form, он автоматически удаляется (и его дочерние элементы управления) при закрытии.

Но этого не происходит, когда вы сами удаляете элементы управления из коллекции элементов управления. Вы не можете использовать метод Clear (), вы должны сделать это так:

  while (panel.Controls.Count > 0) panel.Controls[0].Dispose();

Причина, по которой он работает таким образом, заключается в том, что временем жизни окна управляет Windows, а не ваша программа. Пока окно живое, управляющая оболочка не должна собирать мусор. Windows Forms отслеживает дескрипторы окон во внутренней таблице. Пока Handle действителен, таблица гарантирует, что оболочка класса Control не может быть собрана сборщиком мусора. Другими словами, всегда есть хотя бы одна ссылка на объект Control.

Эта ссылка не удаляется из этой внутренней таблицы до тех пор, пока окно не получит сообщение WM_NCDESTROY, последнее сообщение, которое оконная процедура получает перед уничтожением дескриптора окна. Удаление элемента управления из коллекции элементов управления недостаточно для разрушения окна. Если вы явно не вызовете Dispose (), он превратится в «зомби», невидимое окно, ссылку на обертку Control которого вы не сможете получить.

2 голосов
/ 09 января 2010

Вы можете попробовать удалить коллекцию Controls, прежде чем очистить ее. Вот так ...

private void AddControl(Control control)
{
    foreach (IDisposable control in panel.Controls)
      control.Dispose();

    panel.Controls.Clear();
    control.Size = new Size(panel.Width - 1, panel.Height - 1);
    control.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles. Top;
    panel.Controls.Add(control);
}
1 голос
/ 09 января 2010

Это вполне нормальное поведение из-за сборщика мусора. Память выделяется сразу после создания объектов, но не сразу освобождается. Позже, когда запускается сборщик мусора, он замечает, что некоторые объекты больше не ссылаются на них, поэтому освобождает память.

Этот пост SO содержит несколько ссылок в своих ответах, дающих дополнительную информацию о GC.

...