Як виявити / уникнути витоку пам'яті у вашому (Некерованому) коді? [зачинено]


125

Що стосується керованого коду C / C ++, які найкращі методи виявлення витоків пам'яті? І кодування вказівок уникати? (Наче це все просто;)

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

Я знаю, що це не чудовий спосіб, і є кілька уловів. (Наприклад, якщо ви звільняєте пам'ять, виділену викликом платформи API, ваш розмір розподілу не буде точно відповідати вашому звільненню. Зрозуміло, тоді ми збільшили лічильник при виклику дзвінків API, які виділили пам'ять.)

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


Щодо уникнення витоків, наступна публікація має поради: http://stackoverflow.com/questions/27492/c-memory-management
tonylo


Я використовував цю з візуальною студією для виявлення витоку пам’яті. codeproject.com/KB/applications/visualleakdetector.aspx
tiboo

1
ви шукаєте valgrin (для Linux) або делекер (для windows), також шукаєте візуальний детектор витоку ...
Джон Сміт

для пошуку витоків пам’яті дивіться тут: theunixshell.blogspot.com/2013/11/…
Vijay

Відповіді:


78

Якщо ваш код C / C ++ переноситься на * nix, кілька речей краще, ніж у Valgrind .


1
Valgrind також зараз працює на OS X, тому Linux не є вашим єдиним варіантом.
Майкл Андерсон

1
Valgrind для Linux (і OS X). Якщо ви використовуєте windose - делекер - найкраще!
Джон Сміт

@JordiBunster: Приємно! Але час виконання. З великою базою коду (написаною на мові C у лі) ви в основному перевірите свою програму на те, як вона була розроблена. Зловмисник може кілька тисяч потрібних годин для читання коду, щоб знайти експлуатування витоку пам'яті. Я б очікував автоматизованого інструменту для аналізу вихідного коду, подібного до того, що існує для JavaScript.
користувач2284570

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 і видалити.
Зак

2
@Zach: насправді ви можете змусити це також працювати (для будь-якого коду, який ви фактично складете самі) - див. Прийняту
Роман Старков,

Чи буде це працювати і в режимі випуску?
СП

1
@ user3152463 Ні. Відповідно до документації, вона працюватиме лише для створення налагодження: msdn.microsoft.com/en-us/library/e5ewb1h3(v=vs.71).aspx
Dusty Campbell

Цей рядок неправильний: #define CRTDBG_MAP_ALLOC Це має бути: #define _CRTDBG_MAP_ALLOC
Fallso

37

В C ++: використовуйте RAII. Розумні вказівники, як-от std::unique_ptr, std::shared_ptr- std::weak_ptrце ваші друзі.


1
і std: вектор - це чудова заміна, коли масиви (буфери) розташовані в тій самій функції, що і їм виділена.
KJAWolf

4
Принаймні std :: auto_ptr та boost :: shared_ptr все ще чутливі до витоків.
Джаспер Беккерс

5
Тільки якщо ви їх неправильно використовуєте, хоча я повинен визнати, що для std :: auto_ptr неправильно використовувати його досить просто.
Леон Тіммерманс

2
Хоча це хороша порада щодо стандартів кодування, вона не дає відповіді на питання. Навіть використання shared_ptr може призвести до витоків із круговою залежністю. І ви можете мати "протікання" з необмеженими стратегіями кешування, які застосовуються навіть до мов, зібраних сміттям.
CashCow

@CashCow: ти маєш рацію. Хоча я ще цього не бачив на практиці, це, мабуть, тому, що я їх використовую порядно. Цитуючи відповідь нижче, "Використовуйте вказівники лише тоді, коли це абсолютно необхідно".
Леон Тіммерманс

28

Як розробник C ++, ось декілька простих рекомендацій:

  1. Використовуйте покажчики лише при крайній необхідності
  2. Якщо вам потрібен покажчик, перевірте , якщо розумний покажчик можливість
  3. Використовуйте шаблон GRASP Creator .

Що стосується виявлення витоків пам'яті особисто, я завжди використовував детектор витоку витоку і вважаю це дуже корисним.


2
Візуальний витік Detectore перейшов на новий сайт vld.codeplex.com
KindDragon

VLD - дійсно приємний детектор витоку - я повністю рекомендую його для всіх, хто використовує VC ++
Javid

1
+1 для пункту №1. Це абсолютно принципова річ. На жаль, мені здається, що деякі з найбільших "C ++" бібліотек, як правило, без помітних причин уникають розподілу стеків та / або RAII на користь покажчиків. Отже, вони стають "C з класами", а не C ++.
підкреслити

16

Я використовую DevStudio занадто багато років зараз, і це мене завжди вражає, скільки програмістів не знає про засоби аналізу пам'яті, які доступні в бібліотеках часу налагодження. Ось кілька посилань для початку роботи:

Відстеження запитів на розподіл купи - зокрема розділ про унікальні номери запитів на розподіл

_CrtSetDbgFlag

_CrtSetBreakAlloc

Звичайно, якщо ви не використовуєте DevStudio, це не буде особливо корисним.


10

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


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

7

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


1
Цей інструмент справді ідеальний! Це економить вам проблеми з використанням _CrtDumpMemoryLeaks (); та друзів, як описано в MSDN. Просто один включіть, і це все викриває! Навіть працює в старих бібліотеках С!
m_pGladiator

Нова версія (для VS2013) тут: vld.codeplex.com
Dženan

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 тоді
Хумам Гельфаві

4

Існують різні бібліотеки "malloc" заміни, які дозволять вам викликати функцію в кінці, і вона розповість вам про всю незаплямовану пам'ять, а в багатьох випадках, хто її виправляв (або new'ed) в першу чергу .


4

Якщо ви використовуєте MS VC ++, я настійно рекомендую цей безкоштовний інструмент з кодового проекту: leakfinder від Jochen Kalmbach.

Ви просто додаєте клас до свого проекту та телефонуєте

InitAllocCheck(ACOutput_XML)
DeInitAllocCheck()

до і після коду, який ви хочете перевірити на предмет протікання.

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

PurifyPlus Rational (тепер належить IBM) ілюструє витоки аналогічним чином, але я вважаю, що інструмент витоку фактично простіший у використанні, бо бонус не коштував декількох тисяч доларів!


1
Я перевірив герметичність, і це виглядає нормально, але просто FYI не буде працювати як є для x64, оскільки містить вбудовану збірку.
Зак


3

Якщо ви використовуєте Visual Studio, можливо, варто переглянути Bound Checker . Це не безкоштовно, але це було неймовірно корисно у пошуку протікань у моєму коді. Це не лише витоки пам'яті, але й витоки ресурсів GDI, помилки використання WinAPI та інші речі. Він навіть покаже вам, де ініціалізована просочена пам'ять, що полегшує відстеження витоку.


2

Я думаю, що на це питання немає простої відповіді. Як ви дійсно можете підійти до цього рішення, залежить від ваших вимог. Вам потрібне крос-платформене рішення? Чи використовуєте ви новий / видалити або malloc / безкоштовно (або обидва)? Ви дійсно шукаєте лише "протікання" чи хочете покращити захист, наприклад виявити перевищення буфера (або недоїдання)?

Якщо ви працюєте на вікні Windows, бібліотеки часу налагодження налагодження MS мають деякі основні функції виявлення налагоджень, і як вже вказували інші, є кілька обгортків, які можна включити у ваш джерело, щоб допомогти у виявленні витоків. Пошук пакету, який може працювати як з новим / видаленням, так і з 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: дуже швидко)
  • Поставляється з хорошим графічним відображенням результатів
  • Має й інші корисні можливості: процесорування процесора, профілювання використання пам'яті ...


2

Найкращий захист від протікань - це програмна структура, яка мінімізує використання malloc. Це не тільки добре з точки зору програмування, але також покращує продуктивність та ремонтопридатність. Я не говорю про використання інших речей замість malloc, але з точки зору повторного використання об’єктів та збереження дуже чітких вкладок на всіх об'єктах, що передаються навколо, а не виділення вольово-неволі, як це часто звикає до мов із збирачами сміття як Java.

Наприклад, програма, над якою я працюю, має купу об'єктів кадру, що представляють дані зображення. Кожен об'єкт кадру має піддані, які деструктор кадру звільняє. Програма зберігає список усіх виділених кадрів, і коли їй потрібен новий, перевіряє список невикористаних кадрів кадру, щоб побачити, чи може він повторно використовувати існуючий, а не виділяти новий. При відключенні він просто повторює список, звільняючи все.


2

Я рекомендую використовувати Memory Validator для перевірки програмного забезпечення. Цей інструмент виявив себе неоціненною допомогою, щоб допомогти мені відстежувати витоки пам’яті та покращувати управління пам'яттю програм, над якими я працюю.

Дуже повний і швидкий інструмент.


Програма перевірки пам’яті також надає ім'я файлу та номер рядка для C #, який викликає ваш рідний код. Версія x64 знаходиться в бета-версії
Стівен Келлетт

2

Чи підраховуєте ви алоки та звільнення, інтерполюючи власні функції системного виклику, які записують дзвінки та передають виклик реальній функції?

Це єдиний спосіб відстежувати дзвінки з коду, який ви не написали.

Перегляньте сторінку чоловіка для ld.so. Або ld.so.1 на деяких системах.

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


1

Принаймні для MS VC ++, бібліотека C Runtime має кілька функцій, які я вважав корисними в минулому. Перегляньте довідку MSDN щодо _Crt*функцій.


1

Ммгр Пола Неттел є давно улюбленим моїм інструментом. Ви включаєте mmgr.h у вихідні файли, визначаєте TEST_MEMORY, і він забезпечує текстовий файл, повний проблем із пам'яттю, які виникли під час запуску програми.


1

Загальні правила кодування:

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

1

Засоби налагодження пам’яті варті ваги золота, але за ці роки я виявив, що можна використовувати два простих ідеї, щоб у першу чергу не було кодовано більшість витоків пам'яті / ресурсів.

  1. Напишіть код випуску негайно після написання коду придбання для ресурсів, які потрібно виділити. За допомогою цього методу важче "забути" і в деякому сенсі змушує серйозно думати про життєвий цикл ресурсів, які використовуються наперед, а не як убік.

  2. Використовуйте повернення якомога рідше. Те, що виділено, має бути звільнене лише в одному місці, якщо це можливо. Умовний шлях між придбанням ресурсу та випуском повинен бути спроектований максимально просто та очевидно.


1

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

Що робити, якщо ви щойно помітили, що виробнича система зараз протікає, і ви не знаєте, як відтворити її в тесті? Деякі докази того, що не так, є в стані цієї виробничої системи, і це може бути достатньо, щоб дати зрозуміти, де проблема, щоб ви могли її відтворити.

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

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


1

Працюючи в операційній системі стільникових телефонів Motorola, ми захопили бібліотеку розподілу пам’яті, щоб спостерігати за всіма розподілами пам'яті. Це допомогло знайти багато проблем з розподілом пам'яті. Оскільки профілактика краще, ніж виліковування, я б рекомендував використовувати інструмент статичного аналізу, як Klockwork або PC-Lint


шина - новіша заміна ворсу.
Марк Кегель

@ user14788: продукт PC-Lint від Gimpel набагато сучасніший за старий Unix lint. Він має багато перевірок, характерних для C ++, чого афаїк-шина не робить. Дивіться посилання у відповіді (яку я перейменував із Lint на PC-Lint).
День

0

Valgrind - хороший варіант для Linux. У MacOS X ви можете ввімкнути бібліотеку MallocDebug, яка має декілька варіантів налагодження проблем з розподілом пам'яті (див. Сторінку "malloc", розділ "ЕКОЛОГІЯ" містить відповідні деталі). У SD X SDK також входить інструмент під назвою MallocDebug (зазвичай встановлюється в / Developer / Applications / Performance Tools /), який може допомогти вам контролювати використання та витоки.


0

Виявити:

Налагодження ЕПТ

Уникайте:

Розумні покажчики, boehm GC


0

Приємна заміна malloc, calloc та reallloc - це rmdebug, користуватися досить просто. Це набагато швидше, ніж вальгринг, так що ви можете перевірити свій код широко. Звичайно, у нього є деякі недоліки, після виявлення витоку вам, ймовірно, все ще потрібно скористатися valgrind, щоб знайти місце появи витоку, і ви можете протестувати лише неполадки, які ви робите безпосередньо. Якщо lib протікає через те, що ви неправильно використовуєте, rmdebug його не знайде.

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


0

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


Я не бачу, чому уповільнення робить результати марними. Безумовно, просочена пам'ять просочується незалежно від швидкості, з якою працює програма. Сенс цих інструментів - знайти витоки, тож де проблема? Це запускалося так повільно, що ви не змогли фізично змусити його покрити всі кодові шляхи, щоб їх профілювати?
підкреслити

-1

Здається, Mtrace є стандартним вбудованим для Linux. Етапи:

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

mtrace не дуже корисний для C ++
Ерін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.