Утечки памяти в .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, он будет собирать мусор в какой-то момент. Так что это не способ вызвать утечку памяти.

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

Итак, каковы возможные пути возникновения такой утечки памяти?

Как сказал Кейт, ваш образец не вызывает утечек памяти.

tobsen 24.03.2010 19:32
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
20
1
14 218
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

Невозможно предоставить исчерпывающий список ... это очень похоже на вопрос: "Как ты можешь промокнуть?"

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

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

Как бы вы настроили FxCop, чтобы обеспечить соблюдение этого правила?

Joel 03.10.2014 19:01

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

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

Обновлено: Или это фактические ошибки в сборщике мусора или неуправляемом коде.

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

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

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

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

Ни за что. Разработайте Compact Framework в .Net, и вы быстро обнаружите, что ваше устройство в спешке использует память, если вы не удаляете свои объекты должным образом, поэтому вам не следует здесь обобщать. Вы не можете дождаться, когда это сделает сборщик мусора.

Mat Nadrofsky 12.12.2008 15:25

На самом деле это не вызывает утечек, это просто увеличивает работу сборщика мусора:

// 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 - это большая часть того, что означает управляемый.

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

Очень опасно оставлять Controls лежать без дела. Это нормально для небольших одноразовых приложений, но вы всегда должны избавляться от них в реальных приложениях, иначе вы пожалеете об этом. Поверьте, я был там.

Niki 26.06.2010 00:24

Я наткнулся на это, это опасная вещь - бросать одноразовые предметы! Чтобы проверить это, создайте и отбросьте System.Drawing.Bitmap в цикле - вы очень скоро получите OutOfMemoryException, и GC не поможет.

bohdan_trotsenko 09.12.2011 13:44

@modosansreves - да, это наверняка сломает ваше приложение. Однако из-за того, что вы получаете управляемый OutOfMemoryException, происходит сбой только этого приложения. Утечка неуправляемой памяти приведет к BSOD, зависнет или иным образом приведет к сбою всей машины.

Keith 12.12.2011 16:38

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

Vaibhav 21.08.2008 20:25

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

Ответ принят как подходящий

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

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

Что это обозначает?

Eric Nicholson 13.10.2009 16:09

Финализатор однопоточный. «Завершение» - это то, что происходит, когда объект, который можно удалить, наконец, удаляется. Если один конкретный элемент не может быть удален, то ничего не удаляется, и у вас заканчивается память.

Leon Bambrick 06.11.2009 14:58

Так что же тогда подразумевается под словом «блок» в контексте - переопределить и обернуть код функцию финализатора или полностью предотвратить ее запуск?

Hardryv 27.07.2010 22:17

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

Kieren Johnstone 27.07.2010 22:27

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

Basic 13.12.2010 21:09

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

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

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

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

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

Установка свойства 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 пытается выдать его за законный, хорошо продуманный класс, но я думаю, они просто создали его для устранения фундаментальной утечки, касающейся менеджеров валют и привязки данных к элементам управления сеткой.

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

Злой пост. У нас есть мобильное приложение, которое мы тщательно очистили и позаботились обо всем, что реализует IDisposable. Даже после всего этого у нас все еще был странный сбой в поле после интенсивного использования ... У нас был точный сценарий! Ты жжешь.

Mat Nadrofsky 12.12.2008 15:47

lol +1 за комментарий Мата Надрофского

Drevak 01.12.2009 19:57
  1. Сохранение ссылок на объекты, которые вам больше не нужны.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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