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

125

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

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

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

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

пракаш
источник
Что касается предотвращения утечек, в следующем сообщении есть несколько советов: http://stackoverflow.com/questions/27492/c-memory-management
tonylo
Я задал этот вопрос здесь: http://stackoverflow.com/questions/25730/what-is-the-best-free-memory-leak-detector-for-a-cc-program-and-its-plug-in- dll и имел большой успех с Visual Leak Detector .
Джим Бак,
Я использовал это с Visual Studio для обнаружения утечки памяти. codeproject.com/KB/applications/visualleakdetector.aspx
tiboo
1
вы ищите valgrin (для Linux) или Deleaker (для Windows), также смотрите визуальный детектор утечек ...
Джон Смит,
для поиска утечек памяти проверьте здесь: theunixshell.blogspot.com/2013/11/…
Виджай

Ответы:

78

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

Жорди Бунстер
источник
1
Valgrind теперь также работает в OS X, поэтому Linux - не единственный вариант.
Майкл Андерсон,
1
Valgrind для Linux (и OS X). Если вы используете виндовс - делекер - лучше всего!
Джон Смит
@JordiBunster: Отлично! Но на основе времени выполнения. Имея большую базу кода (написанную на C в случае необходимости), вы в основном будете тестировать свою программу на то, как она была спроектирована. Злоумышленник может потратить несколько тысяч часов на чтение кода, чтобы найти эксплойт утечки памяти. Я ожидал появления автоматизированного инструмента для анализа исходного кода, подобного тому, который существует для JavaScript.
user2284570 02
65

Если вы используете 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 с malloc и free. Подробный отчет, включающий номера строк, не создается, если вы используете c ++ new и delete.
Зак,
2
@Zach: на самом деле вы тоже можете заставить это работать (во всяком случае, для любого кода, который вы действительно компилируете самостоятельно) - см. Принятый ответ в social.msdn.microsoft.com/forums/en-US/vcgeneral/thread/…
Роман Старков,
Будет ли это работать и в режиме выпуска?
СП
1
@ user3152463 Нет. Согласно документации, это будет работать только для отладочной сборки: msdn.microsoft.com/en-us/library/e5ewb1h3(v=vs.71).aspx
Дасти Кэмпбелл
Эта строка неверна: #define CRTDBG_MAP_ALLOC Это должно быть: #define _CRTDBG_MAP_ALLOC
Fallso,
37

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

Леон Тиммерманс
источник
1
и std: vector - отличная замена, когда массивы (буферы) освобождаются в той же функции, в которой они выделяются.
KJAWolf
4
По крайней мере, std :: auto_ptr и boost :: shared_ptr по-прежнему подвержены утечкам.
Джаспер Беккерс,
5
Только если вы используете их неправильно, хотя я должен признать, что для std :: auto_ptr неправильно использовать его довольно легко.
Леон Тиммерманс,
2
Хотя это хороший совет для стандартов кодирования, он не дает ответа на вопрос. Даже использование shared_ptr может привести к утечкам с круговыми зависимостями. И у вас могут быть «утечки» с неограниченными стратегиями кэширования, которые применимы даже к языкам со сборкой мусора.
CashCow
@CashCow: вы правы. Хотя я еще не видел этого на практике, вероятно, потому, что я использую их экономно. Процитируем ответ ниже: «Используйте указатели только в случае крайней необходимости».
Леон Тиммерманс
28

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

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

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

Huppie
источник
2
Visual Leak Detectore переехал на новый сайт vld.codeplex.com
KindDragon
VLD - ДЕЙСТВИТЕЛЬНО хороший течеискатель - я полностью рекомендую его всем, кто использует VC ++
Джавид
1
+1 за пункт №1. Это абсолютно фундаментальная вещь. К сожалению, мне кажется, что некоторые из крупнейших библиотек "C ++" склонны избегать выделения стека и / или RAII в пользу Pointers Everywhere, часто без видимой причины. Таким образом, они в конечном итоге являются «C с классами», а не настоящим C ++.
underscore_d
16

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

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

_CrtSetDbgFlag

_CrtSetBreakAlloc

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

Skizz
источник
10

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

Tal
источник
3
Ссылка больше не работает, попробуйте здесь для документации: support.microsoft.com/kb/2580960 и здесь, чтобы загрузить: microsoft.com/en-us/download/details.aspx?id=26798
JPaget
7

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

Эрнан
источник
1
Этот инструмент действительно идеален! Это избавляет вас от необходимости использовать _CrtDumpMemoryLeaks (); и друзья, как описано в MSDN. Всего одно включение, и он все раскрывает! Работает даже в старых библиотеках Си!
m_pGladiator
Новая версия (для VS2013) находится здесь: vld.codeplex.com
Дженан
7

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

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

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

Серж
источник
1
и тогда не забывайте правило 3 или 5
Humam Helfawi
4

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

Пол Томблин
источник
4

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

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

InitAllocCheck(ACOutput_XML)
DeInitAllocCheck()

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

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

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

Джон Сибли
источник
1
Я проверил утечку, и он выглядит нормально, но, к вашему сведению, он не будет работать как есть для x64, потому что он содержит встроенную сборку.
Зак,
3

Сам никогда не использовал, но мои друзья C говорят мне Purify .

Иэн Холдер
источник
3

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

Herms
источник
2

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

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

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

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

отметка
источник
2

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

  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

Томас Каммейер
источник
2

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

Есть много инструментов, которые делают аналогичный подсчет распределения / свободного места, плюсы Goolge Perftools:

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

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

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

Темный Шикари
источник
2

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

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

Фабьен Хюре
источник
Валидатор памяти также предоставляет имя файла и номер строки для C #, который вызывает ваш собственный код. 64-разрядная версия находится в стадии бета
Стивен Келлетт
2

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

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

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

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

Роб Уэллс
источник
1

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

Дэн Шилд
источник
1

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

Джош Мэтьюз
источник
1

Общие правила кодирования:

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

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

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

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

Эйнштейн
источник
1

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

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

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

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

tialaramex
источник
1

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

аку
источник
Шина - это новая замена ворса.
Марк Кегель,
@ user14788: Продукт Gimpel PC-Lint намного современнее старого Unix lint. Он имеет много проверок, специфичных для C ++, чего нет в afaik splint. См. Ссылку в ответе (который я переименовал с Lint в PC-Lint).
Дэн
0

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

JBL
источник
0

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

Отладка CRT

Избегайте:

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

DrPizza
источник
0

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

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

quinmars
источник
0

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

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

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

  1. установить переменную окружения MALLOC_TRACE в bash
    MALLOC_TRACE = / tmp / mtrace.dat
    export MALLOC_TRACE;
  2. Добавьте #include <mcheck.h> в начало вашего основного исходного файла
  3. Добавить mtrace (); в начале main и muntrace (); внизу (перед оператором возврата)
  4. скомпилируйте свою программу с ключом -g для получения отладочной информации
  5. запустить вашу программу
  6. отображать информацию об утечке с помощью
    mtrace your_prog_exe_name /tmp/mtrace.dat
    ( сначала мне пришлось установить perl-скрипт mtrace в моей системе fedora с помощью yum install glibc_utils   )
оборота бумажная лошадка
источник
mtrace не очень полезен для C ++
Эрин,