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

152

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

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

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

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

Скотт Лэнгхэм
источник
Название вашего сообщения не соответствует вопросу в вашем сообщении. Я предлагаю вам обновить свой заголовок.
Кевин
Ты прав. Извините, я немного устал от текущей утечки, на которую я охотюсь! Название обновлено.
Скотт Лэнгхэм
3
@ Скотт: Не надоедать .NET, это не проблема. Ваш код
GEOCHET
3
Да, мой код или сторонние библиотеки, которые я имею удовольствие использовать.
Скотт Лэнгхэм
@ Скотт: Смотри мой ответ. MemProfiler того стоит. Его использование также даст вам совершенно новый уровень понимания мира .NET GC.
GEOCHET

Ответы:

51

Я использую Scitech MemProfiler, когда подозреваю утечку памяти.

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

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

GEOCHET
источник
3
Да, я попробовал это, и это помогло мне разобраться в некоторых хитрых утечках. Оказалось, что самые большие утечки были вызваны сторонними библиотеками в неуправляемом коде, к которому они обращались посредством взаимодействия. Я был впечатлен, что этот инструмент обнаружил утечки в неуправляемом коде, а также в управляемом коде.
Скотт Лэнгхэм
1
Я принял это как ответ, потому что в конечном итоге это сработало для меня, но я думаю, что все остальные ответы очень полезны. Кстати, этот инструмент чаще называют SciTech's Mem Profiler!
Скотт Лэнгхэм
41

Просто для решения проблемы, связанной с забыванием, попробуйте решение, описанное в этом блоге . Вот суть:

    public void Dispose ()
    {
        // Dispose logic here ...

        // It's a bad error if someone forgets to call Dispose,
        // so in Debug builds, we put a finalizer in to detect
        // the error. If Dispose is called, we suppress the
        // finalizer.
#if DEBUG
        GC.SuppressFinalize(this);
#endif
    }

#if DEBUG
    ~TimedLock()
    {
        // If this finalizer runs, someone somewhere failed to
        // call Dispose, which means we've failed to leave
        // a monitor!
        System.Diagnostics.Debug.Fail("Undisposed lock");
    }
#endif
Джей Базузи
источник
Я бы предпочел бросить исключение вместо Debug.Fail
Pedro77
17

В нашем проекте мы использовали программное обеспечение Ants Profiler Pro от Red Gate. Это работает очень хорошо для всех приложений на основе языка .NET.

Мы обнаружили, что .NET Garbage Collector очень «безопасен» для очистки объектов в памяти (как и должно быть). Это будет держать объекты вокруг только потому, что мы могли бы использовать его когда-нибудь в будущем. Это означало, что нам нужно быть более осторожным с количеством объектов, которые мы надували в памяти. В конце мы преобразовали все наши объекты данных в «раздувание по требованию» (непосредственно перед тем, как запрашивается поле), чтобы уменьшить нагрузку на память и повысить производительность.

РЕДАКТИРОВАТЬ: Вот еще одно объяснение того, что я имею в виду под «надувать по требованию». В нашей объектной модели нашей базы данных мы используем Свойства родительского объекта, чтобы представить дочерний объект (ы). Например, если бы у нас была какая-то запись, которая ссылалась на какую-то другую запись «detail» или «lookup» на индивидуальной основе, мы бы структурировали ее так:

class ParentObject
   Private mRelatedObject as New CRelatedObject
   public Readonly property RelatedObject() as CRelatedObject
      get
         mRelatedObject.getWithID(RelatedObjectID)
         return mRelatedObject
      end get
   end property
End class

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

class ParentObject
   Private mRelatedObject as CRelatedObject
   Public ReadOnly Property RelatedObject() as CRelatedObject
      Get
         If mRelatedObject is Nothing
            mRelatedObject = New CRelatedObject
         End If
         If mRelatedObject.isEmptyObject
            mRelatedObject.getWithID(RelatedObjectID)
         End If
         return mRelatedObject
      end get
   end Property
end class

Это оказалось намного более эффективным, потому что объекты оставались в памяти до тех пор, пока они не были необходимы (был вызван метод Get). Это обеспечило очень большое повышение производительности в ограничении обращений к базе данных и огромный выигрыш в объеме памяти.

отметка
источник
Я второй этот продукт. Это был один из лучших профилировщиков, которые я использовал.
Гор
Я обнаружил, что профилировщик достаточно хорош для анализа проблем с производительностью. Однако инструменты анализа памяти были довольно скудными. Я обнаружил утечку с помощью этого инструмента, но это была чушь, помогающая мне определить причину утечки. И это совсем не поможет, если утечка случится в неуправляемом коде.
Скотт Лэнгхэм
Хорошо, новая версия 5.1 намного лучше. Лучше помочь вам найти причину утечки (хотя есть еще несколько проблем, которые ANTS сказал мне, что они исправят в следующей версии). Тем не менее, по-прежнему не работает неуправляемый код, но если вас не беспокоит неуправляемый код, теперь это довольно хороший инструмент.
Скотт Лэнгхэм
7

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

Зак Гохенур
источник
Ага. CLRPRofiler довольно крутой. Он может стать немного взрывчатым с информацией при попытке копаться в представлении, которое он дает вам выделенных объектов, но все есть. Это определенно хорошая отправная точка, тем более что она бесплатная.
Скотт Лэнгхэм
6

Вы используете неуправляемый код? По словам Microsoft, если вы не используете неуправляемый код, утечки памяти в традиционном смысле невозможны.

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

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

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

Кроме того, проект может только иметь утечку памяти. Это условие может возникнуть, если много больших объектов (таких как объекты DataTable) объявлены и затем добавлены в коллекцию (например, DataSet). Ресурсы, которыми владеют эти объекты, никогда не могут быть освобождены, а ресурсы остаются живыми на протяжении всего запуска программы. Это выглядит как утечка, но на самом деле это всего лишь признак того, как в программе выделяется память.

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

Одна распространенная ошибка - не удалять обработчики событий, которые подписываются на объект. Подписка на обработчик событий предотвратит повторное использование объекта. Кроме того, взгляните на оператор using, который позволяет создать ограниченную область действия для ресурса ресурса.

Тимоти Ли Рассел
источник
5
См. Blogs.msdn.com/tess/archive/2006/01/23/… . Неважно, является ли утечка памяти «традиционной» или нет, это все же утечка.
Константин
2
Я понимаю вашу точку зрения - но неэффективное распределение и повторное использование памяти программой отличается от утечки памяти.
Тимоти Ли Рассел
хороший ответ, спасибо, что вспомнили, что обработчики событий могут быть опасными.
frameworkninja
3
@ Тимоти Ли Рассел: Если неограниченный (1) объем памяти может оставаться одновременно выделенным (укорененным) после того, как он стал бесполезным (2), когда в системе нет информации и импульса, необходимых для своевременного ее удаления, это утечка памяти , Даже если когда-нибудь память может освободиться, если может накопиться достаточно бесполезных вещей, чтобы задушить систему до того, как это произойдет, это утечка. (1) Больше, чем O (N), где N - сумма полезного распределения; (2) Материал бесполезен, если удаление ссылок на него не повлияет на функциональность программы.
суперкат
2
@ Тимоти Ли Рассел: Обычный шаблон «утечки памяти» возникает, когда память удерживается одним объектом от имени другого объекта , ожидая, что ему сообщат, когда он больше не нужен, но последний оставляет объект, не сообщая первый. Сущность, хранящая память, на самом деле не нуждается в этом, но нет способа определить это.
суперкат
5

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

TWK
источник
5

У меня просто была утечка памяти в службе Windows, которую я исправил.

Сначала я попробовал MemProfiler . Я нашел, что это действительно трудно использовать и совсем не удобно для пользователя.

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

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

BillyBob
источник
3

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

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

Крис Баллард
источник
3

Я предпочитаю dotmemory от Jetbrains

josepainumkal
источник
Вы можете быть единственным :)
HellBaby
Я тоже это попробовал. Я думаю, что это хороший инструмент. Простой в использовании, информативный. Интегрируется в Visual Studio
redeye
В нашем случае, при устранении утечек памяти, инструмент Visual Studio Snapshot вылетел / не сделал снимок. Dotmemory сохранял свою хладнокровие и обрабатывал несколько снимков размером более 3 ГБ с (на первый взгляд) легкостью.
Майкл Каргл
3

Большие пушки - Инструменты отладки для Windows

Это удивительная коллекция инструментов. С его помощью вы можете анализировать как управляемые, так и неуправляемые кучи и делать это в автономном режиме. Это было очень удобно для отладки одного из наших приложений ASP.NET, которые продолжали перерабатываться из-за чрезмерного использования памяти. Мне оставалось только создать полный дамп памяти живого процесса, работающего на рабочем сервере, весь анализ проводился в автономном режиме в WinDbg. (Оказалось, что какой-то разработчик злоупотреблял хранилищем памяти в памяти.)

В блоге «Если сломано это ...» есть очень полезные статьи на эту тему.

Constantin
источник
2

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

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

Горд
источник
2

Один из лучших инструментов с помощью средств отладки для Windows , и принимая дамп памяти процесса с использованием ADPlus , а затем использовать WinDbg и Сос плагин для анализа памяти процесса, потоков и стек вызовов.

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

Затем проанализируйте в автономном режиме.

Стюарт Макконнелл
источник
Да, это хорошо работает, особенно для более сложных вещей или диагностики проблем в выпущенном программном обеспечении, к которым вы не можете легко подключить отладчик. В этом блоге есть много советов по правильному использованию этих инструментов: blogs.msdn.com/tess
Скотт Лэнгхэм
2

После одного из моих исправлений для управляемого приложения у меня было то же самое, например, как проверить, что у моего приложения не будет той же утечки памяти после моего следующего изменения, поэтому я написал что-то вроде инфраструктуры проверки выпуска объекта, пожалуйста, посмотрите пакет NuGet ObjectReleaseVerification . Вы можете найти образец здесь https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample и информацию об этом образце http://outcoldman.ru/en/blog/show/322

outcoldman
источник
0

В Visual Studio 2015 рассмотрите возможность использования готового инструмента диагностики использования памяти для сбора и анализа данных об использовании памяти.

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

Майкл Фрейдгейм
источник
0

Один из лучших инструментов, которые я использовал, - DotMemory. Вы можете использовать этот инструмент как расширение в VS. После запуска приложения вы можете анализировать каждую часть памяти (по объектам, именам и т. д.), которую использует ваше приложение, и сделать некоторый снимок этого Сравните это с другими снимками. DotMemory

Rebwar
источник