Стратегии для отслеживания утечек памяти, когда вы сделали все неправильно - PullRequest
7 голосов
/ 02 марта 2009

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

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

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

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

Ответы [ 13 ]

11 голосов
/ 02 марта 2009

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

5 голосов
/ 02 марта 2009

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

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

2 голосов
/ 02 марта 2009

Мне нравится CLR Profiler от Microsoft. Он предоставляет несколько отличных инструментов для визуализации управляемой кучи и отслеживания утечек.

1 голос
/ 02 марта 2009

Получите это: http://www.red -gate.com / Products / ants_profiler / index.htm

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

1 голос
/ 02 марта 2009

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

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

0 голосов
/ 28 января 2012

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

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 / многопоточность также нужно обрабатывать, если это необходимо, но я оставил это для наглядности).

0 голосов
/ 02 марта 2009

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

http://memprofiler.com

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

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

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

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

ИМХО, они оба великолепны.

0 голосов
/ 02 марта 2009

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

0 голосов
/ 02 марта 2009

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

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

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

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

0 голосов
/ 02 марта 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...