Стратегии отслеживания утечек памяти, когда вы все сделали неправильно

StackOverflow https://stackoverflow.com/questions/600757

  •  03-07-2019
  •  | 
  •  

Вопрос

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

Его задача — прочитать группу файлов размером около 2 МБ, выполнить некоторый анализ и замену строк, а затем вывести их в различные форматы.Естественно, это означает большое количество строк, и трассировка памяти показывает, что у меня много строк, и это именно то, чего я ожидал.Структура программы представляет собой серию классов (каждый в своем потоке, поскольку я идиот), который действует на объект, представляющий каждый файл в памяти.(Каждый объект имеет входную очередь, которая использует блокировку на обоих концах.Хотя это означает, что я могу запускать эту простую обработку параллельно, это также означает, что у меня есть несколько объектов размером 2 МБ, находящихся в памяти.) Структура каждого объекта определяется объектом схемы.

Мои классы обработки вызывают события после завершения обработки и передают ссылку на большой объект, содержащий все мои строки, чтобы добавить его в очередь следующего объекта обработки.Замена события вызовом функции для добавления в очередь не останавливает утечку.Один из выходных форматов требует от меня использования неуправляемого объекта.Реализация Dispose() в классе не останавливает утечку.Я заменил все ссылки на объект схемы индексным именем.Никаких кубиков.Я понятия не имел, что является причиной этого, и понятия не имел, где искать.Трассировка памяти не помогает, потому что я вижу только кучу создаваемых строк и не вижу, где ссылки застревают в памяти.

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

Это было полезно?

Решение

Один из методов, который я бы попробовал, — это систематически сокращать объем кода, необходимого для демонстрации проблемы, не устраняя при этом проблему.Это неофициально известно как «разделяй и властвуй» и является мощным методом отладки.Как только у вас появится маленький пример, демонстрирующий ту же проблему, вам будет намного легче понять.Возможно, тогда проблема с памятью прояснится.

Другие советы

Есть только один человек, который может вам помочь.Имя этого человека Тесс Феррандес.(приглушенная тишина)

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

мне нравится Профилировщик CLR от Майкрософт.Он предоставляет отличные инструменты для визуализации управляемой кучи и отслеживания утечек.

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

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

Взять это: http://www.red-gate.com/Products/ants_profiler/index.htm

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

  1. Добавьте код в конструктор объекта Un -Mainaged для регистрации, когда он находится на месте, и сортируйте уникальный идентификатор.Используйте этот уникальный идентификатор, когда объект снова уничтожен, и вы можете, по крайней мере, сказать, какие из них сбиваются с пути.
  2. Грип код для каждого места, которое вы создаете новый объект;Следуйте по этому пути кода, чтобы увидеть, есть ли у вас подходящее уничтожение.
  3. Добавьте указатели цепочки к построенным объектам, чтобы у вас была ссылка на объект, построенный до и после текущего.Тогда вы сможете просмотреть их позже.
  4. Добавьте счетчики ссылок.
  5. Доступен ли «отладочный Malloc»?

Надстройка управляемой отладки СоС (Son of Strike) чрезвычайно эффективен для отслеживания «утечек» управляемой памяти, поскольку их по определению можно обнаружить в корнях gc.

Он будет работать в WinDbg или Visual Studio (хотя в WinDbg его во многих отношениях проще использовать).

С этим совсем не легко справиться.Вот руководство

Я бы поддержал рекомендацию также просмотреть блог Тесс Фернандес.

Откуда вы знаете, что у вас действительно есть утечка памяти?

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

Будьте осторожны с определением «утечки».«Использует больше памяти» или даже «использует слишком много памяти» — это не то же самое, что «утечка памяти».Это особенно актуально в среде со сбором мусора.Возможно, GC просто не нужно было собирать лишнюю память, которую вы видите использованной.Также обратите внимание на разницу между использованием виртуальной и физической памяти.

Наконец, не все «утечки памяти» вызваны проблемами «памяти».Однажды мне сказали (а не попросили) исправить срочную утечку памяти, из-за которой IIS часто перезапускался.Фактически, я выполнил профилирование и обнаружил, что использую много строк через класс StringBuilder.Я реализовал пул объектов (из статьи MSDN) для StringBuilders, и использование памяти существенно снизилось.

IIS по-прежнему перезапускался так же часто.Это произошло потому, что не было утечки памяти.Вместо этого существовал неуправляемый код, который утверждал, что является потокобезопасным, но таковым не был.Использование его в веб-службе (несколько потоков) привело к записи по всей куче библиотеки времени выполнения C.Поскольку никто не искал неуправляемые исключения, никто этого не видел, пока мне не удалось выполнить профилирование с помощью AQtime от Automated QA.У него есть окно событий, в котором отображаются крики боли из библиотеки времени выполнения C.

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

Если ваш неуправляемый объект действительно является причиной утечки, вы можете вызвать его AddMemoryPressure когда он выделяет неуправляемую память и RemoveMemoryPressure в Finalize/Dispose/где бы он ни освобождал неуправляемую память.Это позволит сборщику мусора лучше справиться с ситуацией, поскольку он может не осознавать, что в противном случае необходимо запланировать сбор данных.

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

Лучший инструмент профилирования памяти для .Net:

http://memprofiler.com

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

http://www.yourkit.com/dotnet/download/index.jsp

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

Я работаю над игровым движком реального времени с более чем 700 тысячами строк кода, написанным на C#, и провел сотни часов, используя оба этих инструмента.Я использую продукт Sci Tech с 2002 года и YourKit!за последние три года.Хотя я пробовал немало других, я всегда возвращался к этим.

ИМХО, они оба просто гениальны.

Подобно Чарли Мартину, вы можете сделать что-то вроде этого:

static unigned __int64 _foo_id = 0;
foo::foo()
{
    ++_foo_id;
    if (_foo_id == MAGIC_BAD_ALLOC_ID)
        DebugBreak();
    std::werr << L"foo::foo @ " << _foo_id << std::endl;
}
foo::~foo()
{
    --_foo_id;
    std::werr << L"foo::~foo @ " << _foo_id << std::endl;
}

Если вы сможете воссоздать его, хотя бы один или два раза, с тем же идентификатором выделения, это позволит вам сразу увидеть, что происходит (очевидно, что TLS/потоковая обработка также должна обрабатываться, если это необходимо, но я оставил это для ясность).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top