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

С точки зрения .NET:

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

Ответов (15)

Решение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The best explanation of how the garbage collector works is in Jeff Richters CLR via C# book, (Ch. 20). Reading this gives a great grounding for understanding how objects persist.

One of the most common causes of rooting objects accidentally is by hooking up events outisde a class. If you hook up an external event

e.g.

SomeExternalClass.Changed += new EventHandler(HandleIt);

and forget to unhook to it when you dispose, then SomeExternalClass has a ref to your class.

As mentioned above, the SciTech memory profiler is excellent at showing you roots of objects you suspect are leaking.

But there is also a very quick way to check a particular type is just use WnDBG (you can even use this in the VS.NET immediate window while attached):

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

Now do something that you think will dispose the objects of that type (e.g. close a window). It's handy here to have a debug button somewhere that will run System.GC.Collect() a couple of times.

Then run !dumpheap -stat -type <TypeName> again. If the number didn't go down, or didn't go down as much as you expect, then you have a basis for further investigation. (I got this tip from a seminar given by Ingo Rammer).

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

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

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

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

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

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

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

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

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