Как вы обнаруживаете / избегаете утечек памяти в вашем (неуправляемом) коде?

В неуправляемом коде C/C++, каковы лучшие методы для обнаружения утечек памяти? И руководств по кодированию, чтобы избежать? (Как будто это так просто;)

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

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

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

29 ответов

Если ваш код C/C++ переносим на *nix, немногие вещи лучше, чем Valgrind.

Если вы используете Visual Studio, Microsoft предоставляет некоторые полезные функции для обнаружения и устранения утечек памяти.

Я бы начал с этой статьи: https://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.140).aspx

Вот краткое изложение этих статей. Во-первых, включите эти заголовки:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

Затем вам нужно вызвать это при выходе из вашей программы:

_CrtDumpMemoryLeaks();

Кроме того, если ваша программа не выходит из одного и того же места каждый раз, вы можете вызвать это в начале вашей программы:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

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

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

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

В C++: используйте RAII. Умные указатели, такие как std::unique_ptr, std::shared_ptr, std::weak_ptr твои друзья.

Как разработчик C++, вот несколько простых рекомендаций:

  1. Используйте указатели только тогда, когда это абсолютно необходимо
  2. Если вам нужен указатель, проверьте дважды, если SmartPointer является возможным
  3. Используйте шаблон GRASP Creator.

Что касается обнаружения утечек памяти лично, я всегда использовал Visual Leak Detector и считаю его очень полезным.

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

Отслеживание запросов на выделение кучи - в частности, раздел "Уникальные номера запросов на выделение ресурсов"

_CrtSetDbgFlag

_CrtSetBreakAlloc

Конечно, если вы не используете DevStudio, это не будет особенно полезно.

Я поражен, что никто не упомянул DebugDiag для ОС Windows.
Он работает над выпуском сборок и даже на сайте заказчика.
(Вам просто нужно сохранить свои версии PDB и настроить DebugDiag на использование общедоступного сервера символов Microsoft)

Visual Leak Detector - очень хороший инструмент, хотя он не поддерживает вызовы во время выполнения VC9 (например, MSVCR90D.DLL).

Microsoft VC++ в режиме отладки показывает утечки памяти, хотя и не показывает, где ваши утечки.

Если вы используете C++, вы всегда можете избежать использования явно: у вас есть vector, string, auto_ptr (до C++11; заменено на unique_ptr в C++ 11), unique_ptr (C++11) и shared_ptr (C++11) в вашем арсенале.

Когда новое неизбежно, попробуйте спрятать его в конструкторе (и спрятать delete в деструкторе); то же самое работает для сторонних API.

Если вы используете MS VC++, я настоятельно рекомендую этот бесплатный инструмент из проекта кода: leakfinder от Jochen Kalmbach.

Вы просто добавляете класс в свой проект и вызываете

InitAllocCheck(ACOutput_XML)
DeInitAllocCheck()

до и после кода, который вы хотите проверить на утечки.

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

PurifyPlus от Rational (в настоящее время принадлежит IBM) иллюстрирует утечки аналогичным образом, но я считаю, что инструмент для поиска утечек на самом деле проще в использовании, при этом его премия не стоит нескольких тысяч долларов!

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

Если вы используете Visual Studio, возможно, стоит взглянуть на Bounds Checker. Это не бесплатно, но невероятно помогло найти утечки в моем коде. Это не только утечки памяти, но и утечки ресурсов GDI, ошибки использования WinAPI и другие вещи. Он даже покажет вам, где была инициализирована утечка памяти, что значительно упростит отслеживание утечки.

Никогда не использовал это сам, но мои друзья C говорят мне, Очищают.

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

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

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

  1. Вы можете найти это полезным.

  2. Хотя это немного круто, я не позволяю этому смущать меня.

  3. Несмотря на то, что он привязан к некоторым хукам win32, это легко исправить.

Есть вещи, с которыми вы должны быть осторожны при использовании: не делайте ничего, на что нужно опираться new в исходном коде, остерегайтесь предупреждений о случаях, которые он может пропустить в верхней части файла leakcheck.cpp, понимайте, что если вы включите (и исправите все проблемы с) код, создающий дампы изображений, вы можете создать огромный файл.

Конструкция предназначена для того, чтобы вы могли включать и выключать контролер без перекомпиляции всего, что включает его заголовок. Включите leakcheck.h, где вы хотите отследить проверку и перестроить один раз. После этого скомпилируйте leakcheck.cpp с или без LEAKCHECK #define'd, а затем перезапустите, чтобы включить и выключить его. Включение unleakcheck.h отключит его локально в файле. Предусмотрено два макроса: CLEARALLOCINFO() будет избегать неправильного сообщения об одном и том же файле и строке, когда вы пересекаете выделение кода, который не содержит leakcheck.h. ALLOCFENCE() просто удаляет строку в сгенерированном отчете без какого-либо выделения.

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

Вы можете найти его здесь: http://www.cse.ucsd.edu/~tkammeye/leakcheck.html

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

Это единственный способ отслеживания вызовов, исходящих из кода, который вы не написали.

Загляните на страницу руководства для ld.so. Или ld.so.1 на некоторых системах.

Также сделайте Google LD_PRELOAD, и вы найдете несколько интересных статей, объясняющих эту технику, на www.itworld.com.

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

Очень полный и быстрый инструмент.

Для Linux: попробуйте Google Perftools

Существует множество инструментов, которые выполняют аналогичные подсчеты alloc / free, плюсы Goolge Perftools:

  • Довольно быстро (по сравнению с valgrind: очень быстро)
  • Поставляется с хорошим графическим отображением результатов
  • Имеет другие полезные возможности: профилирование процессора, профилирование использования памяти...

Я думаю, что нет простого ответа на этот вопрос. То, как вы действительно можете подойти к этому решению, зависит от ваших требований. Вам нужно кроссплатформенное решение? Вы используете new/delete или malloc/free (или оба)? Вы действительно ищете только "утечки" или вам нужна лучшая защита, такая как обнаружение переполнения буфера (или переполнения)?

Если вы работаете на стороне Windows, библиотеки среды выполнения отладки MS имеют некоторые базовые функции обнаружения отладки, и, как уже указывалось в другой, в ваш источник может быть включено несколько оболочек, помогающих обнаруживать утечки. Поиск пакета, который может работать как с new/delete, так и с malloc / free, очевидно, дает вам больше гибкости.

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

Но помимо обнаружения утечек, существует понятие обнаружения повреждения памяти посредством переполнения буфера (или опустошения). Этот тип функциональности отладки, я думаю, сложнее, чем простое обнаружение утечек. Этот тип системы также усложняется, если вы работаете с объектами C++, потому что полиморфные классы могут быть удалены различными способами, что приводит к хитрости в определении истинного базового указателя, который удаляется. Я не знаю ни одной хорошей "бесплатной" системы, которая бы обеспечивала достойную защиту от переполнения. мы написали систему (кроссплатформенную) и нашли ее довольно сложной.

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

По крайней мере для MS VC++ библиотека C Runtime имеет несколько функций, которые я нашел полезными в прошлом. Проверьте справку MSDN для _Crt* функции.

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

  1. Напишите код выпуска сразу после написания кода приобретения для ресурсов, которые вы хотите выделить. С этим методом труднее "забыть" и в некотором смысле заставляет всерьез задуматься о жизненном цикле ресурсов, которые используются заранее, а не в стороне.

  2. Используйте возврат как можно скорее. То, что выделено, должно быть освобождено только в одном месте, если это возможно. Условный путь между получением ресурса и выпуском должен быть максимально простым и очевидным.

Общее руководство по кодированию:

  • Ресурсы должны быть освобождены на том же "уровне" (функция / класс / библиотека), где они размещены.
  • Если это невозможно, попробуйте использовать автоматическое освобождение (увеличить общий указатель...)

Работая на операционной системе сотовых телефонов Motorola, мы захватили библиотеку выделения памяти, чтобы наблюдать за распределением памяти. Это помогло найти много проблем с выделением памяти. Поскольку профилактика лучше лечения, я бы рекомендовал использовать инструмент статического анализа, такой как Klockwork или PC-Lint.

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

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

Вот где выборка Монте-Карло входит в картину. Прочитайте статью Рэймонда Чена в блоге "Способ выявления утечек памяти у бедного человека", а затем ознакомьтесь с моей реализацией (предполагается, что Linux протестирован только на x86 и x86-64).

http://github.com/tialaramex/leakdice/tree/master

Большинство профилировщиков памяти замедляют мое большое сложное приложение Windows до такой степени, что результаты бесполезны. Существует один инструмент, который хорошо работает для поиска утечек в моем приложении: UMDH - http://msdn.microsoft.com/en-us/library/ff560206%28VS.85%29.aspx

Valgrind - хороший вариант для Linux. В MacOS X вы можете включить библиотеку MallocDebug, которая имеет несколько опций для отладки проблем с выделением памяти (см. Справочную страницу malloc, раздел "ОКРУЖАЮЩАЯ СРЕДА" содержит соответствующую информацию). OS X SDK также включает в себя инструмент MallocDebug (обычно устанавливаемый в /Developer/Applications/Performance Tools/), который может помочь вам контролировать использование и утечки.

Хорошая замена malloc, calloc и reallloc - это rmdebug, она довольно проста в использовании. Это намного быстрее, чем valgrind, так что вы можете тщательно протестировать свой код. Конечно, у него есть некоторые недостатки: после того, как вы обнаружили утечку, вам, вероятно, все еще нужно будет использовать valgrind, чтобы найти место утечки, и вы можете проверять только malloc, которые вы делаете напрямую. Если библиотека утечка, потому что вы используете ее неправильно, rmdebug не найдет ее.

http://www.hexco.de/rmdebug/

Обнаружение:

Отладка ЭЛТ

Избегайте:

Умные указатели, бем GC

Mtrace является стандартным встроенным для Linux. Шаги:

  1. установить переменную окружения MALLOC_TRACE в bash
    MALLOC_TRACE = / TMP /mtrace.dat
    экспорт MALLOC_TRACE;
  2. Добавьте #include в начало вашего основного исходного файла.
  3. Добавьте mtrace(); в начале main и muntrace(); внизу (перед оператором возврата)
  4. скомпилируйте вашу программу с ключом -g для отладочной информации
  5. запустить вашу программу
  6. показать информацию об утечке с
    mtrace your_prog_exe_name /tmp/mtrace.dat
    (Мне пришлось сначала установить Perl-скрипт mtrace в моей системе fedora с помощью yum install glibc_utils)
Другие вопросы по тегам