Come rilevate / evitate perdite di memoria nel codice (non gestito)? [chiuso]


125

Nel codice C / C ++ non gestito, quali sono le migliori pratiche per rilevare perdite di memoria? E le linee guida per la codifica da evitare? (Come se fosse così semplice;)

Abbiamo usato un po 'un modo stupido in passato: avere un incremento del contatore per ogni chiamata e decremento dell'allocazione di memoria durante la liberazione. Alla fine del programma, il valore del contatore dovrebbe essere zero.

So che questo non è un ottimo modo e ci sono alcune catture. (Ad esempio, se si sta liberando la memoria allocata da una chiamata API della piattaforma, il conteggio delle allocazioni non corrisponderà esattamente al conteggio delle liberazioni. Naturalmente, abbiamo incrementato il contatore quando abbiamo chiamato le chiamate API che hanno allocato la memoria.)

Mi aspetto esperienze, suggerimenti e forse alcuni riferimenti a strumenti che lo semplificano.


Per evitare perdite, il seguente post contiene alcuni consigli: http://stackoverflow.com/questions/27492/c-memory-management
tonylo,


Ho usato questo con Visual Studio per rilevare la perdita di mem. codeproject.com/KB/applications/visualleakdetector.aspx
tiboo

1
tu bruci Valgrin (per Linux) o Deleaker (per Windows), vedi anche il rilevatore visivo di perdite ...
John Smith,

per trovare perdite di memoria, controlla qui: theunixshell.blogspot.com/2013/11/…
Vijay

Risposte:


78

Se il tuo codice C / C ++ è portatile su * nix, poche cose sono meglio di Valgrind .


1
Valgrind ora funziona anche su OS X, quindi Linux non è la tua unica opzione.
Michael Anderson,

1
Valgrind per Linux (e OS X). Se usi windose - deleaker - il migliore di tutti!
John Smith,

@JordiBunster: Nice! Ma basato sul runtime. Con una base di codice di grandi dimensioni (scritta in C in questo caso) testerai principalmente il tuo programma per come è stato progettato. Un utente malintenzionato può leggere le diverse migliaia di ore necessarie a leggere il codice per trovare un exploit di perdita di memoria. Mi sarei aspettato uno strumento automatizzato per l'analisi del codice sorgente simile a quello esistente per JavaScript.
user2284570

65

Se si utilizza Visual Studio, Microsoft fornisce alcune utili funzioni per il rilevamento e il debug delle perdite di memoria.

Vorrei iniziare con questo articolo: https://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.140).aspx

Ecco il breve riassunto di quegli articoli. Innanzitutto, includi queste intestazioni:

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

Quindi è necessario chiamare questo quando il programma termina:

_CrtDumpMemoryLeaks();

In alternativa, se il tuo programma non esce sempre nello stesso posto, puoi chiamarlo all'inizio del programma:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

Ora, quando il programma esce da tutte le allocazioni che non erano libere, verranno stampate nella finestra di output insieme al file in cui sono state allocate e al verificarsi dell'allocazione.

Questa strategia funziona per la maggior parte dei programmi. Tuttavia, in alcuni casi diventa difficile o impossibile. L'uso di librerie di terze parti che eseguono l'inizializzazione all'avvio potrebbe causare la visualizzazione di altri oggetti nel dump della memoria e rendere difficile il rilevamento delle perdite. Inoltre, se una delle tue classi ha membri con lo stesso nome di una qualsiasi delle routine di allocazione della memoria (come malloc), le macro di debug CRT causeranno problemi.

Esistono altre tecniche spiegate nel collegamento MSDN a cui si fa riferimento che potrebbero essere utilizzate anche.


Una nota su questo metodo: sembra che funzioni solo se stai usando C puro con malloc e free. Il report dettagliato che include i numeri di riga non viene creato se si utilizza il nuovo ed elimina c ++.
Zach,

2
@Zach: in realtà puoi farlo funzionare anche tu (per qualsiasi codice che compili tu stesso, comunque) - vedi la risposta accettata in social.msdn.microsoft.com/forums/en-US/vcgeneral/thread/…
Roman Starkov

Funzionerà anche in modalità di rilascio?
JV

1
@ user3152463 No. Secondo la documentazione, funzionerà solo per la build di debug: msdn.microsoft.com/en-us/library/e5ewb1h3(v=vs.71).aspx
Dusty Campbell

Questa riga è errata: #define CRTDBG_MAP_ALLOC Dovrebbe essere: #define _CRTDBG_MAP_ALLOC
Fallso

37

In C ++: usa RAII. Puntatori intelligenti come std::unique_ptr, std::shared_ptr, std::weak_ptrsono i tuoi amici.


1
e std: vector è un ottimo sostituto per quando gli array (buffer) sono deallocati nella stessa funzione che sono allocati.
KJA Wolf il

4
Almeno std :: auto_ptr e boost :: shared_ptr sono ancora suscettibili di perdite.
Jasper Bekkers,

5
Solo se li usi in modo errato, anche se dovrei ammettere che per std :: auto_ptr usarlo in modo errato è abbastanza facile.
Leon Timmermans,

2
Sebbene questo sia un buon consiglio per gli standard di codifica, non risponde alla domanda. Anche l'utilizzo di shared_ptr può portare a perdite con dipendenze circolari. E puoi avere "perdite" con strategie di memorizzazione nella cache illimitate, che si applicano anche alle lingue raccolte di rifiuti.
CashCow,

@CashCow: hai ragione. Anche se non l'ho ancora visto in pratica, probabilmente è perché li sto usando con parsimonia. Per citare la risposta di seguito, "Usa i puntatori solo quando assolutamente necessario".
Leon Timmermans,

28

Come sviluppatore C ++ ecco alcune semplici linee guida:

  1. Utilizzare i puntatori solo quando assolutamente necessario
  2. Se è necessario un puntatore, ricontrollare se uno SmartPointer è possibile
  3. Usa il GRASP modello Creator .

Per quanto riguarda il rilevamento personale delle perdite di memoria, ho sempre usato Visual Leak Detector e lo trovo molto utile.


2
Visual Leak Detectore si è trasferito sul nuovo sito vld.codeplex.com
KindDragon

VLD è un rilevatore di perdite davvero bello - lo consiglio vivamente a tutti coloro che usano VC ++
Javid

1
+1 per il punto 1. Questa è assolutamente la cosa fondamentale. Sfortunatamente, mi sembra che alcune delle più grandi librerie "C ++" tendano a evitare l'allocazione dello stack e / o RAII a favore di Pointers Everywhere, spesso senza motivo. Quindi, finiscono per essere "C con Classi", non C ++ reale.
underscore_d

16

Uso DevStudio da troppi anni ormai e mi stupisce sempre quanti programmatori non conoscono gli strumenti di analisi della memoria disponibili nelle librerie di runtime di debug. Ecco alcuni link per iniziare:

Monitoraggio delle richieste di allocazione dell'heap - in particolare la sezione relativa ai numeri di richiesta di allocazione univoci

_CrtSetDbgFlag

_CrtSetBreakAlloc

Naturalmente, se non stai usando DevStudio, questo non sarà particolarmente utile.



7

Visual Leak Detector è un ottimo strumento, sebbene non supporti le chiamate sui runtime VC9 (ad esempio MSVCR90D.DLL).


1
Questo strumento è davvero perfetto! Ti fa risparmiare la fatica di usare _CrtDumpMemoryLeaks (); e amici, come descritto in MSDN. Solo uno include ed espone tutto! Funziona anche nelle vecchie librerie C!
m_pGladiator,

La nuova versione (per VS2013) è qui: vld.codeplex.com
Dženan,

7

Microsoft VC ++ in modalità debug mostra perdite di memoria, sebbene non mostri dove sono le perdite.

Se si utilizza C ++ si può sempre evitare l'uso di nuovo in modo esplicito: si dispone vector, string, auto_ptr(pre C ++ 11; sostituito da unique_ptrin C ++ 11), unique_ptr(C ++ 11) eshared_ptr (C ++ 11) nel vostro arsenale.

Quando il nuovo è inevitabile, prova a nasconderlo in un costruttore (e nascondi Elimina in un distruttore); lo stesso vale per le API di terze parti.


1
e non dimenticare la regola del 3 o del 5 allora
Humam Helfawi,

4

Esistono diverse librerie "malloc" sostitutive che ti permetteranno di chiamare una funzione alla fine e che ti parlerà di tutta la memoria non ordinata e, in molti casi, di chi l'ha muntaed (o ne ha appena fatto uso) in primo luogo .


4

Se stai usando MS VC ++, posso consigliare vivamente questo strumento gratuito dal codeproject: leakfinder di Jochen Kalmbach.

Devi semplicemente aggiungere la classe al tuo progetto e chiamare

InitAllocCheck(ACOutput_XML)
DeInitAllocCheck()

prima e dopo il codice che si desidera verificare la presenza di perdite.

Dopo aver compilato ed eseguito il codice, Jochen fornisce uno strumento GUI pulito in cui è possibile caricare il file .xmlleaks risultante e navigare nello stack di chiamate in cui è stata generata ogni perdita per dare la caccia alla riga di codice offensiva.

PurifyPlus di Rational (ora di proprietà di IBM) illustra le perdite in modo simile, ma trovo che lo strumento leakfinder sia effettivamente più facile da usare, con il bonus che non costa diverse migliaia di dollari!


1
Ho controllato leakfinder e sembra a posto, ma solo FYI non funzionerà così com'è per x64 perché contiene un assieme in linea.
Zach,


3

Se stai usando Visual Studio, potrebbe valere la pena consultare Bounds Checker . Non è gratuito, ma è stato incredibilmente utile per trovare perdite nel mio codice. Non solo fa perdite di memoria, ma anche perdite di risorse GDI, errori di utilizzo di WinAPI e altre cose. Ti mostrerà anche dove è stata inizializzata la memoria trapelata, rendendo molto più facile rintracciare la perdita.


2

Penso che non ci sia una risposta facile a questa domanda. Il modo in cui potresti davvero affrontare questa soluzione dipende dalle tue esigenze. Hai bisogno di una soluzione multipiattaforma? Stai usando new / delete o malloc / free (o entrambi)? Sei davvero alla ricerca di "perdite" o desideri una protezione migliore, ad esempio il rilevamento di sovraccarichi del buffer (o underrun)?

Se stai lavorando sul lato Windows, le librerie di runtime di debug di MS hanno alcune funzionalità di base per il rilevamento del debug e, come è già stato sottolineato da un'altra, ci sono diversi wrapper che possono essere inclusi nel tuo sorgente per aiutare con il rilevamento delle perdite. Trovare un pacchetto che può funzionare sia con new / delete che con malloc / free ti offre ovviamente maggiore flessibilità.

Non so abbastanza del lato Unix per fornire aiuto, anche se altri ancora.

Ma oltre al semplice rilevamento delle perdite, esiste l'idea di rilevare il danneggiamento della memoria tramite sovraccarico del buffer (o underrun). Penso che questo tipo di funzionalità di debug sia più difficile del semplice rilevamento delle perdite. Questo tipo di sistema è inoltre ulteriormente complicato se si lavora con oggetti C ++ perché le classi polimorfiche possono essere eliminate in vari modi causando difficoltà nel determinare il vero puntatore di base che viene eliminato. Non conosco un buon sistema "libero" che offra una protezione decente per i sovraccarichi. abbiamo scritto un sistema (multipiattaforma) e lo abbiamo trovato piuttosto impegnativo.


2

Vorrei offrire qualcosa che ho usato a volte in passato: un rudimentale controllo delle perdite che è a livello di sorgente e abbastanza automatico. Lo sto dando via per tre motivi:

  1. Potresti trovarlo utile.

  2. Anche se è un po 'krufty, non mi lascio imbarazzare.

  3. Anche se è legato ad alcuni hook win32, dovrebbe essere facile da alleviare.

Ci sono cose di cui devi stare attento quando lo usi: non fare nulla che debba appoggiarsi new al codice sottostante, attenzione agli avvisi sui casi che potrebbero mancare nella parte superiore di leakcheck.cpp, renditi conto che se giri sul (e risolvere eventuali problemi con) il codice che esegue il dump delle immagini, è possibile generare un file enorme.

Il design ha lo scopo di consentire di accendere e spegnere il correttore senza ricompilare tutto ciò che include la sua intestazione. Includi leakcheck.h dove desideri monitorare e ricostruire una volta. Successivamente, compilare leakcheck.cpp con o senza LEAKCHECK # definito e quindi ricollegarlo per accenderlo e spegnerlo. L'inserimento di unleakcheck.h lo disattiverà localmente in un file. Vengono fornite due macro: CLEARALLOCINFO () eviterà di segnalare lo stesso file e la linea in modo inappropriato quando si attraversa l'allocazione di codice che non includeva leakcheck.h. ALLOCFENCE () rilascia una riga nel report generato senza eseguire alcuna allocazione.

Ancora una volta, ti prego di rendermi conto che non lo uso da un po 'di tempo e potresti dover lavorare un po' con esso. Lo sto facendo cadere per illustrare l'idea. Se ci fosse un interesse sufficiente, sarei disposto a elaborare un esempio, aggiornando il codice nel processo e sostituire i contenuti del seguente URL con qualcosa di più bello che includa un elenco decentemente colorato di sintassi.

Puoi trovarlo qui: http://www.cse.ucsd.edu/~tkammeye/leakcheck.html


2

Per Linux: prova Google Perftools

Esistono molti strumenti che eseguono un conteggio allocato / libero simile, i professionisti di Goolge Perftools:

  • Abbastanza veloce (rispetto a valgrind: molto veloce)
  • Viene fornito con una bella visualizzazione grafica dei risultati
  • Ha altre utili funzionalità: profilazione della CPU, profilazione dell'utilizzo della memoria ...


2

La migliore difesa contro le perdite è una struttura del programma che minimizza l'uso del malloc. Ciò non è solo positivo dal punto di vista della programmazione, ma migliora anche le prestazioni e la manutenibilità. Non sto parlando di usare altre cose al posto di malloc, ma in termini di riutilizzo degli oggetti e di mantenere schede molto esplicite su tutti gli oggetti che vengono passati in giro piuttosto che allocare volenti o nolenti come ci si abitua spesso in linguaggi con i garbage collector come Java.

Ad esempio, un programma su cui lavoro ha un mucchio di oggetti frame che rappresentano dati di immagine. Ogni oggetto frame ha dati secondari, che il distruttore del frame libera. Il programma mantiene un elenco di tutti i frame allocati e quando ne ha bisogno uno nuovo, controlla un elenco di oggetti frame inutilizzati per vedere se può riutilizzare uno esistente anziché allocarne uno nuovo. Allo spegnimento, scorre semplicemente l'elenco, liberando tutto.


2

Consiglio di utilizzare Memory Validator dalla verifica del software. Questo strumento si è dimostrato di inestimabile aiuto per aiutarmi a rintracciare le perdite di memoria e migliorare la gestione della memoria delle applicazioni su cui sto lavorando.

Uno strumento molto completo e veloce.


Memory Validator fornisce anche il nome file e il numero di riga per C # che chiama il tuo codice nativo. La versione x64 è in beta
Stephen Kellett,

2

Stai contando le allocazioni e le liberazioni interpolando le tue funzioni di syscall che registrano le chiamate e quindi passano la chiamata alla funzione reale?

Questo è l'unico modo per tenere traccia delle chiamate provenienti dal codice che non hai scritto.

Dai un'occhiata alla pagina man di ld.so. O ld.so.1 su alcuni sistemi.

Fai anche Google LD_PRELOAD e troverai alcuni articoli interessanti che spiegano la tecnica su www.itworld.com.


1

Almeno per MS VC ++, la libreria C Runtime ha diverse funzioni che ho trovato utili in passato. Controllare la guida MSDN per le _Crt*funzioni.


1

Il mmgr di Paul Nettle è uno dei miei strumenti preferiti da molto tempo. Includi mmgr.h nei file di origine, definisci TEST_MEMORY e fornisce un file di testo pieno di problemi di memoria che si sono verificati durante l'esecuzione della tua app.


1

Linee guida generali sulla codifica:

  • Le risorse devono essere allocate nello stesso "livello" (funzione / classe / libreria) in cui sono allocate.
  • Se ciò non è possibile, prova a utilizzare un po 'di deallocazione automatica (aumenta il puntatore condiviso ...)

1

Gli strumenti di debug della memoria valgono il loro peso in oro, ma nel corso degli anni ho scoperto che due semplici idee possono essere utilizzate per impedire che la maggior parte delle perdite di memoria / risorsa venga codificata in primo luogo.

  1. Scrivi il codice di rilascio immediatamente dopo aver scritto il codice di acquisizione per le risorse che desideri allocare. Con questo metodo è più difficile "dimenticare" e in un certo senso costringe a pensare seriamente al ciclo di vita delle risorse che vengono utilizzate in anticipo anziché come un lato.

  2. Usa il ritorno il più sparramente possibile. Ciò che viene assegnato dovrebbe essere liberato in un solo posto, se possibile. Il percorso condizionale tra acquisizione di risorse e rilascio dovrebbe essere progettato per essere il più semplice ed evidente possibile.


1

In cima a questo elenco (quando l'ho letto) c'era valgrind. Valgrind è eccellente se si è in grado di riprodurre la perdita su un sistema di prova. L'ho usato con grande successo.

Cosa succede se hai appena notato che il sistema di produzione sta perdendo in questo momento e non hai idea di come riprodurlo in prova? Alcune prove di ciò che è sbagliato vengono catturate nello stato di quel sistema di produzione e potrebbe essere sufficiente fornire una visione di dove si trova il problema in modo da poterlo riprodurre.

È qui che entra in gioco il campionamento Monte Carlo. Leggi l'articolo del blog di Raymond Chen, "Il modo in cui l'uomo povero identifica le perdite di memoria" e poi controlla la mia implementazione (presuppone che Linux, testato solo su x86 e x86-64)

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


1

Lavorando sul sistema operativo dei telefoni cellulari Motorola, abbiamo dirottato la libreria di allocazione della memoria per osservare tutte le allocazioni di memoria. Ha aiutato a trovare molti problemi con le allocazioni di memoria. Dal momento che prevenire è meglio che curare, consiglierei di usare uno strumento di analisi statica come Klockwork o PC-Lint


splint è un nuovo rimpiazzo per lanugine.
Mark Kegel,

@ user14788: Il prodotto PC-Lint di Gimpel è molto più moderno del vecchio Unix lint. Ha molti controlli specifici per C ++, cosa che non prevede il splint di afaik. Vedi il link nella risposta (che ho rinominato da Lint a PC-Lint).
Dan,

0

Valgrind è una bella opzione per Linux. In MacOS X, puoi abilitare la libreria MallocDebug che ha diverse opzioni per il debug dei problemi di allocazione della memoria (vedi la manpage malloc, la sezione "AMBIENTE" contiene i dettagli pertinenti). L'SDK di OS X include anche uno strumento chiamato MallocDebug (solitamente installato in / Developer / Applicazioni / Performance Tools /) che può aiutarti a monitorare l'utilizzo e le perdite.


0

Rileva:

Debug CRT

Evitare:

Puntatori intelligenti, boehm GC


0

Un buon sostituto di malloc, calloc e reallloc è rmdebug, è piuttosto semplice da usare. Valgrind è molto più veloce, quindi puoi testare ampiamente il tuo codice. Ovviamente ha degli aspetti negativi, una volta che hai trovato una perdita probabilmente avrai ancora bisogno di usare valgrind per trovare dove appare la perdita e puoi testare solo i malloc che fai direttamente. Se una lib perde perché la usi in modo sbagliato, rmdebug non la troverà.

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


0

La maggior parte dei profiler di memoria rallenta la mia grande e complessa applicazione Windows fino al punto in cui i risultati sono inutili. Esiste uno strumento che funziona bene per trovare perdite nella mia applicazione: UMDH - http://msdn.microsoft.com/en-us/library/ff560206%28VS.85%29.aspx


Non vedo perché il rallentamento renda inutili i risultati. Sicuramente la memoria che perde è trapelata indipendentemente dalla velocità con cui il programma funziona. Il punto di questi strumenti è trovare le perdite, quindi dov'è il problema? È stato eseguito così lentamente che non è stato possibile ottenerlo fisicamente per coprire tutti i percorsi di codice al fine di profilarli?
underscore_d

-1

Mtrace sembra essere quello standard incorporato per Linux. I passaggi sono:

  1. imposta la variabile d'ambiente MALLOC_TRACE in bash
    MALLOC_TRACE = / tmp / mtrace.dat
    export MALLOC_TRACE;
  2. Aggiungi #include <mcheck.h> all'inizio del tuo file sorgente principale
  3. Aggiungi mtrace (); all'inizio di main e muntrace (); in fondo (prima della dichiarazione di ritorno)
  4. compila il tuo programma con l'opzione -g per informazioni di debug
  5. esegui il tuo programma
  6. mostra le informazioni sulla perdita con
    mtrace nome_prog_exe /tmp/mtrace.dat
    (ho dovuto installare prima lo script mtrace perl sul mio sistema fedora con yum install glibc_utils   )

mtrace non è molto utile per C ++
Erin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.