Как избежать утечек памяти?

Каковы некоторые советы, которые я могу использовать, чтобы избежать утечек памяти в моих приложениях? Есть ли какие-либо ошибки или ловушки, на которые я могу смотреть?

Ответы

Ответ 1

Вызовите Dispose на объекты IDisposable или используйте предложение using. Это должно позаботиться о большей части утечек, о которых я могу думать.

Ответ 2

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

Ответ 3

Как уже упоминалось, ocdecio обязательно вызовите Dispose on Idisposable objects и не забудьте удалить обработчики событий, когда вы закончите с объектом. При создании классов, которые работают с неуправляемыми ресурсами, не забудьте реализовать Idisposable, поэтому пользователь будет знать, что есть критические ресурсы, которые нужно будет утилизировать.

Кроме того, несмотря на то, что сборка мусора делает для вас небольшую работу, вы должны избавиться от ссылок на объекты, с которыми вы закончили. Иначе у них все равно будет корень, и они не будут GC'ed.

Ответ 4

Я знаю, что некоторые люди собираются советовать сборку мусора в качестве решения. Но есть много случаев, когда сбор мусора не дает вам результатов, которые вы ожидаете. Легко в конечном итоге держаться за беспричинное обращение, которое мешает целым целям памяти освободиться. Прочитайте о том, как это торпедировало запись DARPA Grand Challenge. Вы можете утверждать, что это не утечка памяти, но если программа ожидает освобождения памяти, это все еще проблема. Как и в программировании на языке С, через пару месяцев вы узнаете, как убедиться, что вы не оставляете нежелательных ссылок.

Ответ 5

Утечки памяти - это ошибки, поэтому в целом - на вопрос можно ответить так же, как "как закодировать без ошибок"? В конечном счете - невозможно получить никаких ошибок, но вы можете ограничить вероятность наличия в выпущенном коде.

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

  • Простота - золотой - чем проще код, тем меньше вероятность ошибок или утечек.
  • Будьте осторожны при использовании неуправляемых ресурсов - ручек Windows, соединений DB, объектов GDI и т.д.
  • Использовать для IDisposables
  • Реализация IDisposable для классов, которые несут неуправляемые ресурсы
  • Обязательно удаляйте ссылки на неиспользуемые объекты, включая сложные обработчики событий.

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

Ответ 6

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

Я только что закончил поиск утечки памяти, когда на вкладке WPF размещался элемент управления Windows Form (так как эти вкладки содержали много данных, и вы могли их открывать и закрывать, просто ожидая, пока GC не очистит память на закрыть не было). Я использовал профилировщик YourKit, чтобы сделать снимок памяти до открытия вкладки, открыл вкладку, снова закрыл ее и сделал еще один снимок, Внутри профилировщика вы можете визуально сравнить два моментальных снимка и посмотреть, какие объекты пережили этот процесс, и следовать их ссылкам на корень GC. У меня нет опыта работы с другими профайлерами, но я знаю, что есть несколько вариантов, если YourKit не отвечает вашим потребностям.


Отредактировано для добавления:

Хорошо, это не позволяет избежать утечек памяти, исправляя их. Но я оставлю это здесь, так как я думаю, что это полезная информация, и я не думаю, что разработчики .NET знают об этих инструментах.

Ответ 7

Я столкнулся с проблемами, когда объект (Ping) реализовал Dispose() дважды, реализуя интерфейс IDisposable и наследуя его одновременно. Унаследованный метод ничего не сделал, и в результате вам пришлось отдать объект IDisposable при вызове Dispose() или утечке памяти. Здесь сообщение, которое я написал несколько лет назад, более подробно.

Ответ 8

  • Оберните все, что доступно в используемой конструкции.
  • Избегайте COM-объектов.
  • Убедитесь, что все хендлеры событий удалены должным образом.
  • Убедитесь, что все привязки данных удаляются должным образом.
  • Держите его простым.

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

Если вы подозреваете утечку памяти, используйте профайлер, чтобы узнать, хранятся ли какие-либо объекты дольше, чем необходимо.

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

Ответ 9

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

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

Ответ 10

Большинство утечек памяти, с которыми я столкнулся в .NET, были связаны с использованием COM-объектов и не выпуском их должным образом. Как только я вижу ссылку на COM-объект, я думаю, что "утечка памяти".

Ответ 11

Наиболее распространенный случай, когда память не уничтожается GC, - это когда у вас есть обработчики событий, которые не были отцеплены должным образом. Вы можете отцепить в Dispose(), если хотите.

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

Ответ 12

Как и другие, вызывается Dispose() (или использует оператор using), но дополнительно учитывайте, используют ли классы ресурсы и если они реализуют IDisposeable. Это чаще всего проблема в моем коде: у меня есть класс с членом, который не очищается до GC.

Ответ 14

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

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

Ответ 15

Во-первых, позвольте мне поделиться своим ясным пониманием утечки памяти. Мое определение утечки памяти - это когда у вас есть память, которую вы выделили, и больше не ссылаетесь на нее, поэтому освободить эту память невозможно. Сказав это, невозможно иметь утечку памяти в .net-объектах (экземпляры типов CTS, которые живут в управляемой куче, я имею в виду). Unreferenced memory - это именно то, что GC хочет освободить.

Сказав это, можно иметь более слабое понимание того, что такое утечка памяти. Если вы считаете, что утечка памяти является неконтролируемым ростом используемой памяти, это очень легко. Просто сделайте большое злоупотребление статическими переменными, в основном теми, которые ссылаются на огромные списки. Если вы сохраните эти объекты, GC не будет их чистить, продвигая их к более высоким поколениям и делая их еще сложнее собрать. Несмотря на то, что это не утечка памяти в строгом смысле, в конце концов, это может привести к подобным симптомам. Хорошим способом обнаружить такую ​​ "утечку" является использование CLR Profiler.

Другим источником "утечек" является неправильное использование обработчиков событий, как указано ранее. Каждый раз, когда объект A регистрирует один из своих методов экземпляра с событием на объекте B, объект B заканчивает сохранение косвенной ссылки на объект A, что означает, что, пока B жив, A будет сохранен в живых. Обратите внимание, что здесь нет круглости. Как только ни B, ни A не имеют корневой ссылки, независимо от количества перекрестных ссылок, они в конечном итоге будут собраны.

Наконец, на самом деле можно вызвать утечку памяти в .net, но никогда (по крайней мере теоретически), когда речь идет об управляемой памяти, поскольку GC делает отличную работу по ее очистке. Если какой-либо из ваших объектов поддерживает ссылку на неуправляемую память, например, через interop, тогда эта память должна быть явно очищена. Несоблюдение этого может привести к утечке памяти. Хотя я никогда не испытывал такого сценария, по крайней мере теоретически это может произойти. Как указывалось ранее, объекты, которые содержат такие ссылки, должны реализовывать IDiposable, чтобы очистить эту память, и их использование должно гарантировать, что Dispose вызывается для этой цели, главным образом посредством использования предложения use. Обратите внимание: Dispose не освобождает память объекта, но будет запрашивать у объекта освобождение любой неуправляемой памяти, на которую ссылается.

Один особый вид неуправляемой памяти - это тот, который нужен COM-объектам, используемым в сценариях взаимодействия. Эти объекты доступны через Runtime Callable Wrappers, RCW для друзей, но не имеют Dispose. "Использование" не будет работать. Способ освобождения базовых COM-объектов осуществляется через статический метод:

System.Runtime.InteropServices.Marshal.ReleaseComObject(object);

Поскольку "использование" - это только синтаксический сахар для вызова IDisposable.Dispose() в блоке finally, он не может использоваться с RCW, поэтому не забудьте поместить вызов в ReleaseComObject (объект) в блок finally самостоятельно, вы гарантируете, что это действительно называется.

Ответ 17

Используйте ключевое слово "using" для автоматического вызова метода Dispose() объекта IDisposable.
Для любого COM-взаимодействия вам необходимо вручную освободить все ресурсы.