Утечки памяти в приложении Windows Forms - PullRequest
23 голосов
/ 08 октября 2010

Мы разрабатываем большое приложение .NET Windows Forms.Мы сталкиваемся с проблемой утечки памяти / использования в том, что, несмотря на удаление форм.

Сценарий выглядит так:

  1. Наше приложение использует 60 КБ памяти со списком записей, отображаемых в сетке.
  2. Когда пользователь нажимает на запись, он открывает форму, myform.showDialog, показывает детали.Память скачет с 60 КБ до 105 МБ .
  3. Теперь мы закрываем форму myform, чтобы вернуться к сетке, и располагаем эта форма и установить его на нуль .Память остается при 105 МБ .
  4. Теперь, если мы снова выполним шаг 2, он будет повышаться с 105 МБ до 150 МБ и т. Д.

Как освободить память при закрытии myForm?

Мы уже пробовали GC.Collect() и т. Д., Но без каких-либорезультат.

Ответы [ 8 ]

24 голосов
/ 08 октября 2010

Первое место, где нужно искать утечки, - это обработка событий, а не пропущенные вызовы Dispose().Скажем, ваш контейнер (родительская форма) загружает дочернюю форму и добавляет обработчик для события этой дочерней формы (ChildForm.CloseMe).

Если дочерняя форма предназначена для очистки из памяти, тогда этот обработчик событийнеобходимо удалить, прежде чем он станет кандидатом на сборку мусора.

10 голосов
/ 08 октября 2010

Самым распространенным источником утечек памяти в приложениях Windows Forms являются обработчики событий, которые остаются подключенными после удаления формы ... так что это хорошее место для начала расследования. Инструменты типа http://memprofiler.com/ могут сильно помочь в определении корней для экземпляров, которые никогда не были GC'd.

Что касается вашего звонка в GC.Collect

  1. Это определенно не хорошо делать на практике
  2. Чтобы убедиться, что ваш сбор GC действительно собирает как можно больше, вам нужно сделать несколько проходов и дождаться ожидающих финализаторов, поэтому вызов действительно синхронный.

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

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

Как:

static void Main() {
    var form = new MyForm();
    form.Show();
    form.Close();

    // The GC calls below will do NOTHING, because you still have a reference to the form!
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    GC.WaitForPendingFinalizers();

    // Another thing to not: calling ShowDialog will NOT 
    // get Dispose called on your form when you close it.
    var form2 = new MyForm();
    DialogResult r = form2.ShowDialog();

    // You MUST manually call dispose after calling ShowDialog! Otherwise Dispose 
    // will never get called.
    form2.Dispose();

    // As for grids, this will ALSO result in never releasing the form in
    // memory, because the GridControl has a reference to the Form itself
    // (look at the auto-generated designer code).
    var form3 = new MyForm();
    form3.ShowDialog();
    var grid = form3.MyGrid;

    // Note that if you're planning on actually using your datagrid
    // after calling dispose on the form, you're going to have
    // problems, since calling Dipose() on the form will also call
    // dispose on all the child controls.
    form3.Dispose();
    form3 = null;
}
10 голосов
/ 08 октября 2010

Утилизация форм не обязательно гарантирует, что у вас не утечка памяти. Например, если вы связываете его с набором данных, но по окончании не удаляете набор данных, возможно, у вас будет утечка. Возможно, вам придется использовать инструмент профилирования, чтобы определить, какие одноразовые ресурсы не освобождаются.

Кстати, вызов GC.Collect () - плохая идея. Просто говорю.

1 голос
/ 02 октября 2017

У меня недавно была похожая проблема, когда запущенный таймер сохранял форму в памяти, даже если форма была закрыта.Решением было остановить таймер перед закрытием формы.

0 голосов
/ 08 октября 2010

Убедитесь, что у вас полностью удалены все ссылки на форму. Иногда может случиться, что у вас есть какие-то скрытые ссылки, которые вы не заметили.

Например: если вы присоединяете к внешним событиям из своего диалога, то есть к событиям внешнего окна, если вы забудете отсоединить их, у вас будет оставшаяся ссылка на форму, которая никогда не исчезнет.

Попробуйте этот код в своем диалоге (пример плохого кода ...):

   protected override void OnLoad(EventArgs e)
   {
       Application.OpenForms[0].Activated += new EventHandler(Form2_Activated);
       base.OnLoad(e);
   }

   void Form2_Activated(object sender, EventArgs e)
   {
       Console.WriteLine("Activated!");
   }        

И многократно открывая и закрывая диалоговое окно, вы увидите, что количество строк в консоли увеличивается для каждого вызова. Это означает, что форма остается в памяти, даже если вы вызвали ее для удаления (это следует использовать только для освобождения неуправляемых ресурсов, т. Е. Закрытия файла и тому подобного).

0 голосов
/ 08 октября 2010

Сначала проверьте ссылки на вашу форму. Ваша форма подписывается на какие-либо события? Они считаются ссылками, и если издатель событий живет дольше, чем ваша форма, он будет держать вашу форму рядом (если вы не отмените подписку).

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

Способ проверить это - создать много экземпляров вашей формы и освободить их - попробуйте увеличить утечку, чтобы вы выделяли и выпускали 100 экземпляров. Ваша память продолжает увеличиваться, не снижаясь (если это так, у вас есть проблема), или она в конечном итоге возвращается к нормальной? (вероятно, не проблема).

0 голосов
/ 08 октября 2010

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

0 голосов
/ 08 октября 2010

Я не видел ваш код, но это наиболее вероятный сценарий:

1) Ваша форма закрыта, но есть ссылка на нее, висящую там и не может быть сборщиком мусора.

2) Вы загружаете какой-то ресурс, который не освобождается

3) Вы используете XSLT и компилируете его каждый раз, когда преобразуете

4) У вас есть некоторый пользовательский код, который компилируется и загружается ввыполнения

...