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

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

  • Что такое утечка памяти ?
  • Как вы можете определить, протекает ли ваше приложение? Каковы эффекты?
  • Как вы можете предотвратить утечку памяти?
  • Если у вашего приложения есть утечка памяти, оно исчезнет, ​​когда процесс завершится или будет убит? Или утечки памяти в вашем приложении влияют на другие процессы в системе даже после завершения процесса?
  • А как насчет неуправляемого кода, доступ к которому осуществляется через COM Interop и / или P / Invoke?
1.08.2008 15:12:34
15 ОТВЕТОВ
РЕШЕНИЕ

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

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

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

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

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

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

110
14.05.2018 20:34:56
О, ты любишь книги? Я видел, как автор время от времени появлялся на stackoverflow.
Johnno Nolan 9.10.2009 11:01:05
Некоторые объекты .NET также могут получить root-права и стать недоступными для сбора. Все, что IDSposable должен быть удален из-за этого.
kyoryu 27.06.2010 23:08:39
@kyoryu: Как сам объект коренится?
Andrei Rînea 22.10.2010 23:40:23
@ Андрей: Я думаю, что работающий поток - это, пожалуй, лучший пример того, как сам объект укореняется. Объект, который помещает ссылку на себя в статическом непубличном месте (например, подписка на статическое событие или реализация синглтона с помощью инициализации статического поля), также мог бы укорениться сам по себе, так как нет очевидного способа ... гм ... "искоренить" его от швартовки.
Jeffrey Hantin 20.01.2011 07:10:01
@ Джеффри, это необычный способ описать происходящее, и мне это нравится!
Exitos 28.06.2011 08:34:03

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

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

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

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

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

35
1.08.2008 17:52:46

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

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

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

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

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

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

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

32
26.01.2012 15:51:52

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

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

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

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

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

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

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

19
1.08.2008 17:57:14

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

19
19.01.2016 10:30:03

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

16
16.08.2008 19:54:30

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

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

например

SomeExternalClass.Changed += new EventHandler(HandleIt);

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

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

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

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

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

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

15
19.01.2016 10:25:19

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

14
1.08.2008 15:19:25

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

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

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

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

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

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

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

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

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

11
6.05.2017 09:04:13

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

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

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

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

10
5.08.2008 22:47:49

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

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

9
4.08.2008 14:09:47

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

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

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

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

8
30.11.2015 21:14:09

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

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

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

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

7
26.01.2012 15:52:26

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

6
20.08.2008 18:39:25

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

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

1
15.12.2014 16:00:47