Утечки памяти в.NET

Каковы все возможные способы утечки памяти в.NET?

Я знаю о двух:

  1. Не правильно отменена регистрация обработчиков событий / делегатов.
  2. Не использовать динамические дочерние элементы управления в Windows Forms:

Пример:

// Causes Leaks  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  

// Correct Code  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  
label.Dispose();

Обновление: идея состоит в том, чтобы перечислить распространенные подводные камни, которые не слишком очевидны (например, выше). Обычно считается, что утечки памяти не являются большой проблемой из-за сборщика мусора. Не так, как раньше в C++.


Отличная дискуссия, ребята, но позвольте мне уточнить... по определению, если в.NET не останется ссылки на объект, это будет сборщик мусора через некоторое время. Так что это не способ вызвать утечки памяти.

В управляемой среде я бы посчитал утечку памяти, если бы у вас была непреднамеренная ссылка на любой объект, о котором вы не знаете (отсюда два примера в моем вопросе).

Итак, каковы различные возможные способы утечки памяти?

14 ответов

Решение

Заблокируйте поток финализатора. Никакие другие объекты не будут собираться мусором, пока поток финализатора не будет разблокирован. Таким образом, объем используемой памяти будет расти и расти.

Дополнительное чтение: http://dotnetdebug.net/2005/06/22/blocked-finalizer-thread/

Это на самом деле не вызывает утечек, это просто делает больше работы для GC:

// slows GC
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  

// better  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  
label.Dispose();

// best
using( Label label = new Label() )
{ 
    this.Controls.Add(label);  
    this.Controls.Remove(label);  
}

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

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

Установка свойства GridControl.DataSource напрямую без использования экземпляра класса BindingSource ( http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.aspx).

Это вызвало утечки в моем приложении, что заняло у меня довольно много времени, чтобы отследить с помощью профилировщика, в конце концов я нашел этот отчет об ошибке, на который Microsoft ответила: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=92260

Забавно, что в документации по классу BindingSource Microsoft пытается выдать его за законный, хорошо продуманный класс, но я думаю, что они просто создали его, чтобы устранить фундаментальную утечку в отношении управляющих валютами и привязки данных к элементам управления сеткой.

Следите за этим, держу пари, что из-за этого существует множество утечек приложений!

Там нет никакого способа предоставить полный список... это очень похоже на вопрос "Как вы можете промокнуть?"

Тем не менее, убедитесь, что вы вызываете Dispose() для всего, что реализует IDisposable, и убедитесь, что вы реализуете IDisposable для любых типов, которые потребляют неуправляемые ресурсы любого вида.

Время от времени запускайте что-то вроде FxCop в своей кодовой базе, чтобы помочь вам реализовать это правило - вы будете удивлены, насколько глубоко некоторые одноразовые объекты будут скрыты в рамках приложения.

Исключения в методах Finalize (или Dispose для вызовов из Finaliser), которые препятствуют правильному удалению неуправляемых ресурсов. Обычно это связано с тем, что программист предполагает, что объекты порядка будут расположены, и пытается освободить уже удаленные объекты, что приводит к исключению, а остальная часть метода Finalize/Dispose from Finalize не вызывается.

У меня есть 4 дополнительных пункта, чтобы добавить к этому обсуждению:

  1. Завершение потоков (Thread.Abort()), которые создали элементы управления пользовательского интерфейса без надлежащей подготовки к такому событию, может привести к ожидаемому использованию памяти.

  2. Доступ к неуправляемым ресурсам через Pinvoke и их очистка могут привести к утечке памяти.

  3. Модификация больших строковых объектов. Не обязательно утечка памяти, когда-то вне области, GC позаботится об этом, однако, с точки зрения производительности, ваша система может получить удар, если большие строки часто изменяются, потому что вы не можете действительно зависеть от GC, чтобы гарантировать, что след вашей программы минимален.

  4. Создание объектов GDI часто для выполнения пользовательских чертежей. Если вы часто выполняете работу с GDI, используйте один объект gdi повторно.

Вы говорите о неожиданном использовании памяти или фактических утечках? Два перечисленных вами случая не совсем утечки; это случаи, когда объекты держатся дольше, чем предполагалось.

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

Изменить: Или они являются фактическими ошибками в сборщике мусора или неуправляемый код.

Изменить 2: Еще один способ думать об этом, это всегда убедиться, что внешние ссылки на ваши объекты будут выпущены соответствующим образом. Внешний означает код вне вашего контроля. Любой случай, когда это происходит, является случаем, когда вы можете "утечь" память.

Чтобы предотвратить утечки памяти.NET:

1) Используйте конструкцию 'using' (или конструкцию 'try-finally) всякий раз, когда создается объект с интерфейсом'IDisposable'.

2) Сделайте классы IDisposable, если они создают поток или добавляют объект в статическую или долгоживущую коллекцию. Помните, что C# 'event' - это коллекция.

Вот небольшая статья о советах по предотвращению утечек памяти.

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

Одна вещь, которая была действительно неожиданной для меня, это:

Region oldClip = graphics.Clip;
using (Region newClip = new Region(...))
{
    graphics.Clip = newClip;
    // draw something
    graphics.Clip = oldClip;
}

Где утечка памяти? Хорошо, вы должны были распорядиться oldClip, тоже! Так как Graphics.Clip это одно из редких свойств, которое возвращает новый одноразовый объект каждый раз, когда вызывается геттер.

У Тесс Фернандес есть отличные посты в блоге о поиске и устранении утечек памяти. Лаборатория 6 Лаборатория 7

  1. Хранить ссылки на объекты, которые вам больше не нужны.

Другие комментарии - один из способов обеспечить вызов Dispose - это использовать использование... когда структура кода позволяет это делать.

Многие вещи, которые могут вызывать утечки памяти в неуправляемых языках, все еще могут вызывать утечки памяти в управляемых языках. Например, неправильная политика кэширования может привести к утечкам памяти.

Но, как сказали Грег и Дэнни, нет исчерпывающего списка. Все, что может привести к удержанию памяти после ее полезного срока службы, может вызвать утечку.

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

Зафиксированный поток финализатора предотвратит запуск всех оставшихся финализаторов и, таким образом, предотвратит возврат всех финализируемых объектов (так как они все еще укоренены в свободном списке).

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

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

Существует ряд объектов, которые необходимо освободить вручную. Например, удаленные объекты без аренды и сборок (необходимо выгрузить AppDomain).

Другие вопросы по тегам