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

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

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

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

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

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

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

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

#define _CRTDBG_MAP_ALLOC #include  #include  

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

 _CrtDumpMemoryLeaks(); 

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

 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); 

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

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

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

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

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

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

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

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

Отслеживание запросов на размещение кучи – в частности, раздел «Уникальные номера запросов на размещение»

_CrtSetDbgFlag

_CrtSetBreakAlloc

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

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

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

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

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

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

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

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

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

 InitAllocCheck(ACOutput_XML) DeInitAllocCheck() 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Отладка CRT

Избегайте:

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

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

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

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

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
    (Мне пришлось сначала установить скрипт mtrace perl в моей системе fedora с yum install glibc_utils )
Давайте будем гением компьютера.