Подтвердить что ты не робот

Утечка памяти в приложении Winforms

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

Сценарий похож:

  • В нашем приложении используется 60K памяти со списком записей, отображаемых в сетке.
  • Когда пользователь нажимает на запись, открывается форма myform.showDialog, показывающая детали. Память прыгает от от 60 до 105 М
  • Теперь мы закрываем форму myform, чтобы вернуться в сетку, и удалить, и установить значение null. Память остается на 105M.
  • Теперь, если мы снова выполним шаг 2, он переместится с 105M на 150M и т.д.

Может кто-нибудь сказать, как мы можем освободить память, когда мы закрываем myForm? Любая помощь в этом отношении будет высоко оценена. Мы уже пробовали GC.Collect() и т.д., Но результата не было.

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

Ответ 3

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

Как для вашего вызова GC.Collect

  • Это определенно нехорошо сделать это на практике.
  • Чтобы ваш сборник 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;
}

Ответ 4

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

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

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

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

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

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

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

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

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

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

Ответ 8

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