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


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

  • что это утечка памяти?
  • как вы можете определить, течет ли ваше приложение? Каковы последствия?
  • как можно предотвратить утечку памяти?
  • Если ваше приложение имеет утечку памяти, она уходит, когда процесс завершается или убивается? Или утечки памяти в вашем приложении влияют на другие процессы в системе даже после завершения процесса?
  • а как насчет неуправляемых код, доступный через COM-взаимодействие и / или P / Invoke?
15 163

15 ответов:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

утечка уходит, когда процесс завершается, и как насчет com-взаимодействия?

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

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

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

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

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

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

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

надеюсь, что это помогает.

Если вам нужно диагностировать утечку памяти в .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

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

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

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

например

SomeExternalClass.Changed += new EventHandler(HandleIt);

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

как уже упоминалось выше,SciTech memory profiler отлично показывает вам корни объектов, которые вы подозреваете, протекают.

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

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

теперь сделайте что-то, что вы думаете, будет утилизировать объекты этого типа (например, закройте окно). Это удобно здесь, чтобы иметь кнопку отладки где-то это будет работать System.GC.Collect() пару раз.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Я нашел.Net Memory Profiler очень хорошая помощь при поиске утечек памяти в .Net. It не бесплатно, как Microsoft CLR Profiler, но быстрее и более к месту, на мой взгляд. А

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

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