Когда WinForm освобождает свои ресурсы? (С #) - PullRequest
2 голосов
/ 11 июля 2009

Это вопрос C # WinForm. У меня есть дочерняя форма MDI, когда форма открыта, она будет генерировать много таблиц для отображения в DataGridView. Таблицы могут быть большими, и я использую DataBinding для соединения таблиц с DataGridView. Когда я закрываю форму, я замечаю, что память, занятая формой, своевременно восстанавливается.

Я использую обычный способ отображения дочерней формы MDI как:

var f = new FormBigMemory(objPassedIn);
f.Show();

Как показано здесь, я не могу явно вызвать метод Dispose () формы. Я предполагаю, что когда f выходит из своего жизненного цикла, .net автоматически восстанавливает память, которую он занимает.

Поскольку форма занимает много памяти, я хочу явно вызвать GC.Collect (); (Я знаю, что это может быть не очень хорошая идея). Для этого я изменяю код для отображения формы в диалоговой модели, используя следующий код:

using(var f = new FormBigMemoryDialog(objPassedIn);
{
    f.ShowDialog();
}

GC.Collect();

Я очень разочарован тем, что память, занятая формой, не освобождается после вызова GC.Collect (). Я использую инструмент профилирования памяти, чтобы получить снимок памяти после закрытия формы и вызова GC.Collect (). Самая большая память поддерживается BindingList! Я действительно не понимаю: если вся форма удалена, почему BindingList все еще существует в памяти. Я проверил свой код, и нет места, где ссылка BindingList просочилась из формы.

Есть идеи, почему эта вещь со мной происходит? Большое спасибо за любые предложения и советы по управлению памятью .net.

Edit:

Спасибо за множество ответов. Позвольте мне прояснить некоторые моменты.

  1. Я не хочу распоряжаться списком BindingList. BindingList используется в качестве массива в моем коде, поэтому я ожидаю увидеть восстановление памяти, хранящейся в BindingList. Мой плохой, чтобы не сказать ясно.

  2. Я понимаю, что не нужно называть действие GC самостоятельно. Но в моем случае я хочу явно увидеть восстановление памяти, удерживаемое формой сразу после закрытия формы, в любом случае, чтобы сделать это? Пример кода?

  3. В моей задаче я могу многократно открывать одну и ту же форму несколько раз, и каждый раз, когда я закрываю форму, потребление памяти увеличивается, пока не будет выдан OutOfMemory. Это явно не правильно. Я ожидаю увидеть, что после закрытия формы память возвращается к исходному уровню, и я могу многократно открывать форму и закрывать ее, не увеличивая объем памяти (резко).

Редактировать 2:

Я дополнительно исследовал код. Самый большой массив (BindingList) не освобождается после закрытия формы. На него ссылается BindingSource, а на BindingSource - EventHandler, CurrencyManager и пользовательский элемент управления. BindingSource и пользовательский элемент управления указываются в (закрытой) форме, а (закрытая) форма указывается некоторыми обработчиками событий.

Выше приведен снимок памяти после того, как я закрыл форму. Я использую модель диалога, чтобы открыть форму, и после ее закрытия вызывается ее метод Dispose (), и после этого я также вызываю GC.Collect (), чтобы принудительно восстановить память. Почему после этого экземпляр формы все еще существует в моем снимке памяти ??

Ответы [ 7 ]

3 голосов
/ 11 июля 2009

Никогда не звоните в GC.Collect, если у вас нет для этого веских причин.

Я не знаю деталей, но вот что я знаю:

  • Что-то удаляется после удаления всех ссылок.
  • GC не будет пытаться удалить все вещи одновременно, он не хочет мешать работе программы.
  • Есть большая вероятность, что форма просто скрыта, а не закрыта. Как вы закрываете форму?
  • Как говорит Митч Уит , обработчики событий могут быть упрямыми, так как вы часто забываете их удалить.
  • Утилизация не приводит к удалению формы из памяти, она вызывает простой метод внутри формы, возможно, вы можете вставить в нее свою собственную логику очистки.
2 голосов
/ 11 июля 2009

Вы уверены, что отключили все обработчики событий, которые использует форма?

Обновление в ответ на вопрос в комментариях: всякий раз, когда объект добавляет обработчик события к событию (+ = EventHandler), должен быть соответствующий (- = EventHandler), когда вы закончите с экземпляром объекта. В противном случае он будет действовать как корневой объект в куче, и сборщик мусора не будет возвращен.

0 голосов
/ 11 июля 2009

Сборщик мусора работает в свое время.

WinForm и DataGridView - это отдельные, отдельные объекты.

BindingList является частью DataGridView и не имеет ничего общего с формой.

Вам необходимо избавиться от DataGridView, когда WinForm, содержащий DataGridView, закрыт.

DataGridView.Dispose () должен вызываться в соответствующем месте в вашей Форме, возможно, в событии FormClosing.

0 голосов
/ 11 июля 2009

Не ожидайте, что .NET сразу соберет вашу память.

Прочитайте , и , , чтобы получить более полное представление о том, как .NET обрабатывает GC.

И посмотрите на этот , чтобы получить более полное представление о том, как решить вашу проблему. Особенно читать раздел «Что делать, если объекты выжили».

0 голосов
/ 11 июля 2009

Разве профилировщик памяти не говорит вам, что содержит BindingList? Если это не так, этот ответ может быть полезен.

0 голосов
/ 11 июля 2009

Почему вы ожидаете, что Form.Dispose () очистит BindingList? Если вы говорите о BindingList из System.ComponentModel, он даже не одноразовый. Я не уверен, насколько глубоко Form использует свои ресурсы. Он может перебирать свои коллекции Controls и вызывать Dispose для элементов управления, которые реализуют IDisposable. Однако, если вы определяете поле в форме, например DataSet или List, и заполняете его большим количеством данных, вызов Dispose для этой формы не повлияет на память, занятую этим полем. Если вы хотите, чтобы это поле было GCed, то вам просто нужно убедиться, что на него просто нет ссылок из любого места

Как вы используете objPassedIn? Возможно ли, что этот объект хранит ссылки и таким образом не позволяет GC собирать их? Я также рекомендую явно очистить объекты в событии FormClosing, задав для них значение null и / или вызвав для них Dispose. Как упомянул @Mitch, отказ от подписки на события для объектов, которые имеют более длительное время жизни, чем ваша форма, также может быть причиной того, почему GC не собирает ожидаемые вами объекты. Помните, что вы можете вызывать Dispose для формы, но до тех пор, пока у вас есть ссылка на форму, она будет жить и не будет собирать мусор.

TestForm frm = new TestForm();
frm.MyTextField = "Hello World";
frm.ShowDialog();
frm.Dispose();
string textField = frm.MyTextField;

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

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

0 голосов
/ 11 июля 2009

Единственное, что я делаю по-другому для вас, - это использование var при создании новой формы. Я бы использовал имя класса как обычное определение.

FormBigMemory f = new FormBigMemory(objPassedIn);
f.MdiParent = this;
f.Show();

Есть ли вероятность, что с помощью var (который, как я понимаю, на самом деле предназначен только для обобщений) вы каким-то образом сохраняете доступную память? Я не совсем понимаю, как это произойдет, но это может стоить поиска в Google.

Или, возможно, не определяя MdiParent, вы сохраняете форму в памяти?

Если бы кто-нибудь еще мог пролить свет на мои идеи, было бы неплохо. Попробуйте решить эту проблему, ребята.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...