Анатомия "утечки памяти"

В .NET перспективе:

  • Что такое Утечка памяти?
  • Как вы можете определить, протекает ли ваше приложение? Каковы последствия?
  • Как предотвратить утечку памяти?
  • Если ваше приложение имеет утечку памяти, оно исчезает, когда процесс завершается или уничтожается? Или утечка памяти в приложении влияет на другие процессы в системе даже после завершения процесса?
  • А как насчет неуправляемого кода, доступного через COM Interop и/или P/Invoke?

Ответ 1

Лучшее объяснение, которое я видел, приведено в главе 7 бесплатного Основы программирования ebook.

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

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

Понимание модели памяти .NET - ваш лучший способ избежать этого. В частности, понимание того, как работает сборщик мусора и как работают ссылки (опять же, я отсылаю вас к главе 7 книги). Кроме того, помните об общих ловушках, возможно, наиболее распространенных событиях. Если объект A зарегистрирован для события на объекте B, тогда объект A будет придерживаться, пока объект B не исчезнет, ​​потому что B содержит ссылку на A. Решение состоит в том, чтобы отменить регистрацию событий, когда вы закончите.

Конечно, хороший профиль памяти позволит вам увидеть ваши графические объекты и изучить вложенность/привязку ваших объектов, чтобы увидеть, откуда идут ссылки и какой корневой объект отвечает (профиль муравьев red-gate, JetBrains dotMemory, memprofiler - действительно хороший выбор, или вы можете использовать text-only windbg и sos, но я настоятельно рекомендую коммерческий/визуальный продукт, если вы не настоящий гуру).

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

Ответ 2

Строго говоря, утечка памяти потребляет память, которая "больше не используется" программой.

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

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

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

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

Ответ 3

Я думаю, что ответы на вопросы "Что такое утечка памяти" и "что такое эффекты" уже получили ответы, но я хотел добавить еще несколько вопросов по другим вопросам...

Как понять, протекает ли ваше приложение

Один интересный способ - открыть perfmon и добавить трассировки для # байта во всех коллекциях heaps и # Gen 2, в каждом случае глядя только на ваш процесс. Если выполнение определенной функции приводит к увеличению общего количества байтов, и эта память остается выделенной после следующей коллекции Gen 2, вы можете сказать, что функция теряет память.

Как предотвратить

Были высказаны другие хорошие мнения. Я бы просто добавил, что, пожалуй, наиболее часто игнорируемой причиной утечек памяти .NET является добавление обработчиков событий к объектам без их удаления. Обработчик событий, прикрепленный к объекту, является формой ссылки на этот объект, поэтому предотвратит сбор даже после того, как все остальные ссылки исчезнут. Всегда помните, чтобы отсоединить обработчики событий (используя синтаксис -= в С#).

Протекает ли утечка при выходе из процесса, а что касается COM-взаимодействия?

Когда ваш процесс завершается, вся память, отображаемая в ее адресное пространство, восстанавливается ОС, включая любые COM-объекты, обслуживаемые из DLL. Сравнительно редко объекты COM могут обслуживаться отдельными процессами. В этом случае, когда ваш процесс завершается, вы все равно можете нести ответственность за память, выделенную в любых обработанных сервером COM-серверах.

Ответ 4

Я бы определял утечки памяти как объекта, не освобождающего всю память, выделенную после ее завершения. Я обнаружил, что это может произойти в вашем приложении, если вы используете Windows API и COM (т.е. Неуправляемый код с ошибкой в ​​нем или не управляется правильно), в рамках и в сторонних компонентах. Я также обнаружил, что не переваривал после использования некоторых объектов, таких как ручки, которые могут вызвать проблему.

Я лично пережил исключения из памяти, которые могут быть вызваны, но не являются исключительными для утечек памяти в сетевых сетях. (OOM также может поступать от пиннинга, см. Pinning Artical). Если вы не получаете ошибки OOM или вам нужно подтвердить, что это утечка памяти, вызывающая его, тогда единственный способ - профилировать ваше приложение.

Я также попытался бы обеспечить следующее:

a) Все, что реализует Idisposable, расположено либо с помощью блока finally, либо с помощью оператора using, который включает в себя кисти, ручки и т.д. (некоторые люди утверждают, что ничего не добавляют)

b) Все, что имеет метод close, закрывается снова, используя finally, или оператор using (хотя я нашел, что использование не всегда близко зависит, если вы объявили объект вне оператора using)

c) Если вы используете неуправляемый API кода/окон, с которыми они обрабатываются правильно. (некоторые из них очищают методы для освобождения ресурсов)

Надеюсь, что это поможет.

Ответ 5

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

http://msdn.microsoft.com/en-us/magazine/cc163833.aspx

http://msdn.microsoft.com/en-us/magazine/cc164138.aspx

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

У Microsoft также есть более новый инструмент, помогающий создавать аварийные дампы, чтобы заменить ADPlus под названием DebugDiag.

http://www.microsoft.com/downloads/details.aspx?FamilyID=28bd5941-c458-46f1-b24d-f60151d875a3&displaylang=en

Ответ 6

Использование CLR Profiler из Microsoft http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=en - отличный способ определить, какие объекты хранят память, какой поток выполнения приводит к созданию эти объекты, а также мониторинг того, какие объекты живут там, где на куче (фрагментация, LOH и т.д.).

Ответ 7

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

Ответ 8

Лучшее объяснение того, как работает сборщик мусора, находится в книге Джеффа Рихтера CLR через С# (глава 20). Чтение этого дает большую основу для понимания того, как сохраняются объекты.

Одна из наиболее распространенных причин случайного заражения объектов - это подключение событий к классу. Если вы подключаете внешнее событие

например.

SomeExternalClass.Changed += new EventHandler(HandleIt);

и забыть отцепляться от него, когда вы распоряжаетесь, тогда у SomeExternalClass есть ссылка на ваш класс.

Как упоминалось выше, Profi-профилирование SciTech отлично показывает, что вы корни объектов, которые вы подозреваете, протекают.

Но есть и очень быстрый способ проверить конкретный тип - просто использовать WnDBG (вы даже можете использовать его в непосредственном окне VS.NET при подключении):

.loadby sos mscorwks
!dumpheap -stat -type <TypeName>

Теперь сделайте то, что, по вашему мнению, будет уничтожать объекты такого типа (например, закройте окно). Здесь удобно иметь кнопку отладки где-нибудь, которая будет запускать System.GC.Collect() пару раз.

Затем запустите !dumpheap -stat -type <TypeName> снова. Если число не опустилось или не упало так сильно, как вы ожидаете, тогда у вас есть основа для дальнейшего расследования. (Я получил этот совет от семинара, представленного Ingo Rammer).

Ответ 9

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

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

StreamReader sr;
using(sr = new StreamReader("somefile.txt"))
{
    //do some stuff
}

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

Ответ 10

Почему люди думают, что утечка памяти в .NET не такая же, как любая другая утечка?

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

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

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

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

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

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

Я вижу, что у многих это есть, и я действительно надеюсь, что это закончится. Вы не можете попросить пользователя закрыть приложение, чтобы очистить свой беспорядок! Взгляните на браузер, который может быть IE, FF и т.д., Затем откройте, скажем, Google Reader, пусть он останется на несколько дней и посмотрит, что произойдет.

Если затем открыть другую вкладку в браузере, перейдите на какой-либо сайт, а затем закройте вкладку, на которой размещена другая страница, которая заставила браузер течь, вы думаете, что браузер выпустит память? Не так с IE. На моем компьютере IE будет легко есть 1 гигабайт памяти за короткий промежуток времени (около 3-4 дней), если я использую Google Reader. Некоторые газетные страницы еще хуже.

Ответ 11

Я соглашусь с Бернардом, как в .net, что утечка памяти будет.

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

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

Неуправляемый код - это собственный зверь, и если в нем существует утечка, он будет следовать стандартным mem. определение утечки.

Ответ 12

Все утечки памяти устраняются завершением программы.

Утечка достаточно памяти, и операционная система может решить решить проблему от вашего имени.

Ответ 13

Также имейте в виду, что .NET имеет две кучи, одна из которых является большой кучей объектов. Я считаю, что объекты размером около 85 тыс. Или более накладываются на эту кучу. Эта куча имеет разные правила жизни, чем обычная куча.

Если вы создаете большие структуры памяти (словарь или список), было бы разумно искать, что такое точные правила.

Что касается восстановления памяти при завершении процесса, если только ваш запуск Win98 или его эквивалент, все возвращается к ОС при завершении. Единственными исключениями являются вещи, которые открывают кросс-процесс, а другой процесс все еще имеет открытый ресурс.

COM-объекты могут быть сложными. Если вы всегда используете шаблон IDispose, вы будете в безопасности. Но я столкнулся с несколькими сборками interop, которые реализуют IDispose. Ключевым моментом здесь является вызов Marshal.ReleaseCOMObject, когда вы закончите с ним. COM-объекты по-прежнему используют стандартный подсчет ссылок COM.

Ответ 14

Я нашел .NET Memory Profiler - очень хорошая помощь при обнаружении утечек памяти в .Net. Он не свободен, как профилировщик Microsoft CLR, но, по-моему, быстрее и точнее. А

Ответ 15

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

Для получения дополнительной информации посетите http://all-about-java-and-weblogic-server.blogspot.in/2014/01/what-is-memory-leak-in-java.html.