Come trovare la perdita di memoria in un codice / progetto C ++?


180

Sono un programmatore C ++ sulla piattaforma Windows. Sto usando Visual Studio 2008.

Di solito finisco nel codice con perdite di memoria.

Normalmente trovo la perdita di memoria controllando il codice, ma è ingombrante e non è sempre un buon approccio.

Dal momento che non posso permettermi uno strumento di rilevamento delle perdite di memoria a pagamento, volevo che voi ragazzi suggerissi i migliori modi possibili per evitare perdite di memoria.

  1. Voglio sapere come il programmatore può trovare perdite di memoria.
  2. Esistono standard o procedure da seguire per garantire che non vi siano perdite di memoria nel programma?

29
"Di solito finisco nel codice con perdita di memoria." Se si utilizzano variabili automatiche, contenitori e puntatori intelligenti (e si seguono le migliori pratiche per l'utilizzo di puntatori intelligenti), le perdite di memoria dovrebbero essere estremamente rare. Ricorda, in quasi tutti i casi, dovresti usare la gestione automatica delle risorse .
James McNellis,


1
@Hostile Forcella: "Come si può evitare di di solito finiscono in codice con perdite di memoria" non è coperto da queste risposte.
Doc Brown,

2
@Doc Brown: non aveva voglia di guardare che fino troppo, ma è tutto coperto altrove, come ad esempio stackoverflow.com/questions/45627/...
HostileFork dice fiducia Dont SE

1
Rilevatore di perdite fai-da-te: potresti inserire un codice sospetto in un ciclo infinito e quindi aprire un task manager, in genere anche una piccola perdita riempirà la memoria in pochi secondi o minuti (dipende dalla complessità del codice e dalla CPU). Se ciò non accade, quel pezzo di codice probabilmente non perde.
Ciao mondo,

Risposte:


270

Istruzioni

Le cose che ti serviranno

  • Competenza in C ++
  • Compilatore C ++
  • Debugger e altri strumenti software investigativi

1

Comprendere le basi dell'operatore. L'operatore C ++ newalloca la memoria dell'heap. L' deleteoperatore libera la memoria dell'heap. Per ogni new, dovresti usare a in deletemodo da liberare la stessa memoria che hai allocato:

char* str = new char [30]; // Allocate 30 bytes to house a string.

delete [] str; // Clear those 30 bytes and make str point nowhere.

2

Riassegna la memoria solo se hai eliminato. Nel codice seguente, stracquisisce un nuovo indirizzo con la seconda allocazione. Il primo indirizzo viene perso irrimediabilmente, così come i 30 byte a cui puntava. Ora sono impossibili da liberare e hai una perdita di memoria:

char* str = new char [30]; // Give str a memory address.

// delete [] str; // Remove the first comment marking in this line to correct.

str = new char [60]; /* Give str another memory address with
                                                    the first one gone forever.*/

delete [] str; // This deletes the 60 bytes, not just the first 30.

3

Guarda quei compiti puntatore. Ogni variabile dinamica (memoria allocata sull'heap) deve essere associata a un puntatore. Quando una variabile dinamica viene dissociata dai suoi puntatori, diventa impossibile da cancellare. Ancora una volta, ciò si traduce in una perdita di memoria:

char* str1 = new char [30];

char* str2 = new char [40];

strcpy(str1, "Memory leak");

str2 = str1; // Bad! Now the 40 bytes are impossible to free.

delete [] str2; // This deletes the 30 bytes.

delete [] str1; // Possible access violation. What a disaster!

4

Fai attenzione con i puntatori locali. Un puntatore dichiarato in una funzione viene allocato nello stack, ma la variabile dinamica a cui punta viene allocata nell'heap. Se non lo elimini, persisterà dopo che il programma uscirà dalla funzione:

void Leak(int x){

char* p = new char [x];

// delete [] p; // Remove the first comment marking to correct.

}

5

Presta attenzione alle parentesi quadre dopo "elimina". Utilizzare deleteda solo per liberare un singolo oggetto. Utilizzare delete []con parentesi quadre per liberare un array di heap. Non fare qualcosa del genere:

char* one = new char;

delete [] one; // Wrong

char* many = new char [30];

delete many; // Wrong!

6

Se la perdita è ancora consentita, di solito la cerco con Deleaker (controlla qui: http://deleaker.com ).


3
scusa per il commento-domanda, ma per quanto riguarda i parametri di funzione senza puntatori? someFunction("some parameter")devo cancellare "some parameter"in someFunction, dopo la chiamata di funzione, o questi vengono cancellati automaticamente?
19

1
grazie per il link a Deleaker, questo è uno strumento davvero utile con una chiara integrazione in Visual Studio. Potrei risparmiare molto tempo usando. mi ha indicato le linee in cui ho allocato la memoria e non l'ho liberata. Grande. Ed è economico, rispetto ad altri cercatori di perdite di memoria che ho trovato.
questo

@ john smith plz spiega qual è il modo corretto di gestire casi simili al caso 3; str2 = str1; // Male! Ora i 40 byte sono impossibili da liberare. come eliminare str 1 quindi ??
Nihar,

1
Che cosa succede se utilizziamo un tipo di valore come char *, int, float, ... e struct come Vector, CString e non utilizziamo alcun "nuovo" operatore, non causerà perdite di memoria, giusto?
123,

Sono qui solo per dire che non ho toccato c ++ per quasi 14 anni ... ma sono orgoglioso di dire che ho capito e ricordo come fare tutto questo grazie a un libro c ++ che ancora possiedo e leggo quando ' m annoiato da c #. Quel libro è il C ++ effettivo di Scott Mitchell. Dio, ho adorato quel libro. Grazie Scott!
JonH,

33

È possibile utilizzare alcune tecniche nel codice per rilevare la perdita di memoria. Il modo più comune e più semplice per rilevare è, definire una macro dire, DEBUG_NEW e usarlo, insieme a macro predefinite come __FILE__e __LINE__per individuare la perdita di memoria nel codice. Queste macro predefinite indicano il file e il numero di riga delle perdite di memoria.

DEBUG_NEW è solo un MACRO che di solito è definito come:

#define DEBUG_NEW new(__FILE__, __LINE__)
#define new DEBUG_NEW

In modo che ovunque tu usi new , può anche tenere traccia del file e del numero di riga che potrebbero essere utilizzati per localizzare la perdita di memoria nel tuo programma.

E __FILE__, __LINE__sono macro predefinite che valutano rispettivamente il nome del file e il numero di riga dove li usi!

Leggi il seguente articolo che spiega la tecnica di utilizzo di DEBUG_NEW con altre macro interessanti, in modo molto bello:

Un rilevatore di perdite di memoria multipiattaforma


Da Wikpedia ,

Debug_new si riferisce a una tecnica in C ++ per sovraccaricare e / o ridefinire l'operatore new ed operator delete per intercettare le allocazioni di memoria e le chiamate di deallocazione, e quindi eseguire il debug di un programma per l'utilizzo della memoria. Implica spesso la definizione di una macro denominata DEBUG_NEW e rende il nuovo qualcosa di simile al nuovo (_ FILE _, _ LINE _) per registrare le informazioni sul file / riga sull'allocazione.Microsoft Visual C ++ utilizza questa tecnica nelle sue classi di Microsoft Foundation. Esistono alcuni modi per estendere questo metodo per evitare l'uso della ridefinizione delle macro pur essendo in grado di visualizzare le informazioni sul file / linea su alcune piattaforme. Esistono molte limitazioni intrinseche a questo metodo. Si applica solo a C ++ e non può rilevare perdite di memoria da funzioni C come malloc. Tuttavia, può essere molto semplice da usare e anche molto veloce, rispetto ad alcune soluzioni di debugger di memoria più complete.


4
questo #defineincasinerà con sovraccarico operator newe genererà errori del compilatore. Anche se riuscirai a superarlo, le funzioni sovraccaricate non verranno comunque risolte. Sebbene la tecnica sia buona, a volte ha bisogno di molte modifiche al codice.
iammilind,

1
@iammilind: Naturalmente, questa tecnica non è una soluzione completa per tutti i problemi e sicuramente non è applicabile in tutte le situazioni.
Nawaz,

@Chris_vr: auto_ptrnon funzionerà con contenitori standard come std::vector, std::listecc. Vedi questo: stackoverflow.com/questions/111478/…
Nawaz

Va bene, d'accordo. FILE e linea sono descritti. Che cos'è operator newe quali sono queste versioni che stai utilizzando?

14

Esistono alcune tecniche di programmazione ben note che ti aiuteranno a ridurre al minimo il rischio di avere perdite di memoria in prima persona:

  • se è necessario eseguire la propria allocazione dinamica della memoria, scrivere newe deletesempre in coppia, e assicurarsi che il codice di allocazione / deallocazione sia chiamato in coppia
  • evitare l'allocazione dinamica della memoria, se possibile. Ad esempio, utilizzare vector<T> tladdove possibile anzichéT* t = new T[size]
  • usa "puntatori intelligenti" come boost smart pointer ( http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/smart_ptr.htm )
  • il mio preferito personale: assicurati di aver compreso il concetto di proprietà di un puntatore e assicurati che ovunque tu usi i puntatori, sai quale entità di codice è il proprietario
  • scopri quali costruttori / operatori di assegnazione vengono creati automaticamente dal compilatore C ++ e cosa significa se hai una classe che possiede un puntatore (o cosa significa se hai una classe che contiene un puntatore a un oggetto che non possiede).

Uso auto_pointer di un oggetto significa che eliminerà tutti gli altri puntatori oggetto di classe al suo interno.
Chris_vr,

@Chris_vr: se hai una domanda specifica su auto_pointer, ti suggerirei di fare una nuova domanda, incluso un esempio.
Doc Brown,

Molti post mi dicono che vector <> non garantisce la liberazione della memoria. Ho testato personalmente materiale di scambio ecc. E sono giunto alla conclusione che il vettore <> perde soprattutto se usato in modo dinamico. Non capisco come il vettore <> possa essere avvisato sull'allocazione dinamica fai-da-te usando 'nuovo' e pulendo correttamente. Nei miei programmi incorporati evito di usare vector <> per cose dinamiche a causa di tutte le perdite. Lì uso new o std :: list
bart s

Digito un secondo comando a causa del numero di caratteri. Sfortunatamente nel mio c ++ incorporato ho un vecchio c ++ (98?) Che non ha shrink_to_fit su un vettore ... Tuttavia il programma incorporato è sicuro al 100% di bloccarsi completamente quando si esaurisce la memoria usando vector <> in modo dinamico
bart s


8
  1. Scarica gli strumenti di debug per Windows .
  2. Utilizzare l' gflagsutilità per attivare le tracce dello stack in modalità utente.
  3. Utilizzare UMDHper eseguire più istantanee della memoria del programma. Scatta un'istantanea prima che la memoria venga allocata e scatta una seconda istantanea dopo un punto in cui ritieni che il tuo programma abbia perso memoria. Potresti voler aggiungere pause o istruzioni nel tuo programma per darti la possibilità di eseguire UMDHe scattare le istantanee.
  4. Esegui di UMDHnuovo, questa volta nella sua modalità che fa una differenza tra le due istantanee. Verrà quindi generato un rapporto contenente le pile di chiamate di sospette perdite di memoria.
  5. Ripristina le tue gflagsimpostazioni precedenti quando hai finito.

UMDHti fornirà più informazioni rispetto all'heap di debug CRT perché sta monitorando le allocazioni di memoria durante l'intero processo; può anche dirti se i componenti di terze parti perdono.


1
Preferisco Deleaker e Valgrind invece del profiler standard
z0r1fan

8

L'esecuzione di "Valgrind" può:

1) Aiuta a identificare le perdite di memoria - mostra quante perdite di memoria hai e indica le righe nel codice in cui è stata allocata la memoria trapelata.

2) Indicare tentativi errati di liberare memoria (ad es. Chiamata impropria di delete)

Istruzioni per l'uso di "Valgrind"

1) Ottieni valgrind qui .

2) Compilare il codice con -gflag

3) Nella corsa della shell:

valgrind --leak-check=yes myprog arg1 arg2

Dove "myprog" è il vostro programma compilato e arg1, arg2gli argomenti del programma.

4) Il risultato è un elenco di chiamate a malloc/ newche non hanno avuto chiamate successive per l'eliminazione gratuita.

Per esempio:

==4230==    at 0x1B977DD0: malloc (vg_replace_malloc.c:136)

==4230==    by 0x804990F: main (example.c:6)

Ti dice in quale linea mallocè stato chiamato (che non è stato liberato).

Come sottolineato da altri, assicurarsi che per ogni new/ mallocchiamata, si abbia una delete/ freechiamata successiva .


6

Se usi gcc, c'è gprof disponibile.

Volevo sapere come il programmatore trova la perdita di memoria

Alcuni utilizzano strumenti, altri fanno ciò che fai, anche attraverso la revisione del codice peer

Esistono standard o procedure da seguire per garantire che non vi siano perdite di memoria nel programma

Per me: ogni volta che creo oggetti allocati dinamicamente, inserisco sempre il codice di liberazione dopo, quindi riempio il codice tra. Ciò andrebbe bene se sei sicuro che non ci saranno eccezioni nel codice tra. Altrimenti, faccio uso di try-finally (non uso C ++ frequentemente).


qualche volta non possiamo cancellare allocati nel costruttore. cosa fare in quell'occasione.
Chris_vr,

5
  1. In Visual Studio è presente un rilevatore incorporato per perdite di memoria chiamato C Runtime Library. Quando il programma termina dopo il ritorno della funzione principale, CRT verificherà l'heap di debug dell'applicazione. se hai ancora dei blocchi allocati sull'heap di debug, allora hai una perdita di memoria.

  2. Questo forum discute alcuni modi per evitare perdite di memoria in C / C ++ ..


5

Cerca le occorrenze nel tuo codice newe assicurati che si verifichino tutte all'interno di un costruttore con una corrispondente eliminazione in un distruttore. Assicurati che questa sia l'unica possibile operazione di lancio in quel costruttore. Un modo semplice per farlo è avvolgere tutti i puntatori std::auto_ptro boost::scoped_ptr(a seconda che sia necessario spostare la semantica). Per tutto il codice futuro, assicurati solo che ogni risorsa sia di proprietà di un oggetto che pulisce la risorsa nel suo distruttore. Se hai bisogno di spostare la semantica, puoi eseguire l'upgrade a un compilatore che supporta i riferimenti di valore r (credo VS2010) e creare costruttori di spostamento. Se non vuoi farlo, puoi usare una varietà di tecniche complicate che implicano un uso coscienzioso dello swap o provare la libreria Boost.Move.


non è sempre possibile eliminare la memoria allocata nel costruttore. Come gestire questa situazione
Chris_vr

@Chris_vr Cosa intendi? Se tutti i membri del puntatore sono scope_ptrs e ciascuno è inizializzato individualmente, tutti quelli che sono stati costruiti correttamente elimineranno i loro puntatori e gli altri non manterranno comunque i puntatori nella memoria allocata. Ne farò un esempio tra poche ore quando torno a casa dal lavoro.
Mankarse,

@Chris_vr: se hai un esempio specifico, pubblicalo come una nuova domanda, così possiamo discuterne lì.
Doc Brown,

5

È possibile utilizzare lo strumento Valgrind per rilevare perdite di memoria.

Inoltre, per trovare la perdita in una particolare funzione, utilizzare exit (0) alla fine della funzione e quindi eseguirla con Valgrind

`$` valgrind ./your_CPP_program 

5

Un sondaggio sui controllori automatici di perdite di memoria

In questa risposta, confronto diversi controllori di perdite di memoria in un semplice esempio di perdita di memoria di facile comprensione.

Prima di tutto, vedi questa enorme tabella nel wiki di ASan che confronta tutti gli strumenti conosciuti dall'uomo: https://github.com/google/sanitizers/wiki/AddressSanitizerComparisonOfMemoryTools/d06210f759fec97066888e5f27c7e722832b0924

L'esempio analizzato sarà:

main.c

#include <stdlib.h>

void * my_malloc(size_t n) {
    return malloc(n);
}

void leaky(size_t n, int do_leak) {
    void *p = my_malloc(n);
    if (!do_leak) {
        free(p);
    }
}

int main(void) {
    leaky(0x10, 0);
    leaky(0x10, 1);
    leaky(0x100, 0);
    leaky(0x100, 1);
    leaky(0x1000, 0);
    leaky(0x1000, 1);
}

GitHub a monte .

Proveremo a vedere con che chiarezza i diversi strumenti ci indicano le chiamate che perdono.

tcmalloc da gperftools di Google

https://github.com/gperftools/gperftools

Utilizzo su Ubuntu 19.04:

sudo apt-get install google-perftools
gcc -ggdb3 -o main.out main.c -ltcmalloc
PPROF_PATH=/usr/bin/google-pprof \
  HEAPCHECK=normal \
  HEAPPROFILE=ble \
  ./main.out \
;
google-pprof main.out ble.0001.heap --text

L'output dell'esecuzione del programma contiene l'analisi della perdita di memoria:

WARNING: Perftools heap leak checker is active -- Performance may suffer
Starting tracking the heap
Dumping heap profile to ble.0001.heap (Exiting, 4 kB in use)
Have memory regions w/o callers: might report false leaks
Leak check _main_ detected leaks of 272 bytes in 2 objects
The 2 largest leaks:
Using local file ./main.out.
Leak of 256 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581d3 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start
Leak of 16 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581b5 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start


If the preceding stack traces are not enough to find the leaks, try running THIS shell command:

pprof ./main.out "/tmp/main.out.24744._main_-end.heap" --inuse_objects --lines --heapcheck  --edgefraction=1e-10 --nodefraction=1e-10 --gv

If you are still puzzled about why the leaks are there, try rerunning this program with HEAP_CHECK_TEST_POINTER_ALIGNMENT=1 and/or with HEAP_CHECK_MAX_POINTER_OFFSET=-1
If the leak report occurs in a small fraction of runs, try running with TCMALLOC_MAX_FREE_QUEUE_SIZE of few hundred MB or with TCMALLOC_RECLAIM_MEMORY=false, it might help find leaks more re
Exiting with error code (instead of crashing) because of whole-program memory leaks

e l'output di google-pprofcontiene l'analisi dell'utilizzo dell'heap:

Using local file main.out.
Using local file ble.0001.heap.
Total: 0.0 MB
     0.0 100.0% 100.0%      0.0 100.0% my_malloc
     0.0   0.0% 100.0%      0.0 100.0% __libc_start_main
     0.0   0.0% 100.0%      0.0 100.0% _start
     0.0   0.0% 100.0%      0.0 100.0% leaky
     0.0   0.0% 100.0%      0.0 100.0% main

L'output ci indica due delle tre perdite:

Leak of 256 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581d3 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start
Leak of 16 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581b5 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start

Non sono sicuro del motivo per cui il terzo non si è presentato

In ogni caso, quando di solito quando qualcosa perde, succede molte volte, e quando l'ho usato su un progetto reale, ho appena finito per essere segnalato alla funzione di perdita molto facilmente.

Come indicato sull'output stesso, ciò comporta un significativo rallentamento dell'esecuzione.

Ulteriore documentazione su:

Vedi anche: Come utilizzare TCMalloc?

Testato in Ubuntu 19.04, google-perftools 2.5-2.

Indirizzo Sanitizer (ASan) anche di Google

https://github.com/google/sanitizers

Precedentemente menzionato su: Come trovare la perdita di memoria in un codice / progetto C ++? TODO vs tcmalloc.

Questo è già integrato in GCC, quindi puoi semplicemente fare:

gcc -fsanitize=address -ggdb3 -o main.out main.c
./main.out 

e output di esecuzione:

=================================================================
==27223==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 4096 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f210 in main /home/ciro/test/main.c:20
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

Direct leak of 256 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f1f2 in main /home/ciro/test/main.c:18
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

Direct leak of 16 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f1d4 in main /home/ciro/test/main.c:16
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

SUMMARY: AddressSanitizer: 4368 byte(s) leaked in 3 allocation(s).

che identifica chiaramente tutte le perdite. Bello!

ASan può anche fare altri controlli interessanti come le scritture fuori limite: Rilevato smashing dello stack

Testato in Ubuntu 19.04, GCC 8.3.0.

Valgrind

http://www.valgrind.org/

Precedentemente menzionato su: https://stackoverflow.com/a/37661630/895245

Uso:

sudo apt-get install valgrind
gcc -ggdb3 -o main.out main.c
valgrind --leak-check=yes ./main.out

Produzione:

==32178== Memcheck, a memory error detector
==32178== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==32178== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==32178== Command: ./main.out
==32178== 
==32178== 
==32178== HEAP SUMMARY:
==32178==     in use at exit: 4,368 bytes in 3 blocks
==32178==   total heap usage: 6 allocs, 3 frees, 8,736 bytes allocated
==32178== 
==32178== 16 bytes in 1 blocks are definitely lost in loss record 1 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091B4: main (main.c:16)
==32178== 
==32178== 256 bytes in 1 blocks are definitely lost in loss record 2 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091D2: main (main.c:18)
==32178== 
==32178== 4,096 bytes in 1 blocks are definitely lost in loss record 3 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091F0: main (main.c:20)
==32178== 
==32178== LEAK SUMMARY:
==32178==    definitely lost: 4,368 bytes in 3 blocks
==32178==    indirectly lost: 0 bytes in 0 blocks
==32178==      possibly lost: 0 bytes in 0 blocks
==32178==    still reachable: 0 bytes in 0 blocks
==32178==         suppressed: 0 bytes in 0 blocks
==32178== 
==32178== For counts of detected and suppressed errors, rerun with: -v
==32178== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

Quindi, ancora una volta, sono state rilevate tutte le perdite.

Vedi anche: Come posso usare valgrind per trovare perdite di memoria?

Testato in Ubuntu 19.04, valgrind 3.14.0.


4

Visual Leak Detector (VLD) è un sistema di rilevamento perdite di memoria open source gratuito, robusto per Visual C ++.

Quando si esegue il programma con il debugger di Visual Studio, Visual Leak Detector genererà un rapporto di perdita di memoria al termine della sessione di debug. Il rapporto sulle perdite include lo stack di chiamate completo che mostra come sono stati allocati eventuali blocchi di memoria trapelati. Fare doppio clic su una linea nello stack di chiamate per passare a quel file e linea nella finestra dell'editor.

Se hai solo crash dump, puoi usare il !heap -lcomando Windbg , rileverà blocchi heap trapelati. Meglio aprire l'opzione gflags: "Crea database traccia stack modalità utente", quindi vedrai lo stack di chiamate di allocazione della memoria.


4

MTuner è uno strumento gratuito di profilazione della memoria multipiattaforma, rilevamento perdite e analisi che supporta i compilatori MSVC, GCC e Clang. Le caratteristiche includono:

  • cronologia basata sulla cronologia dell'utilizzo della memoria e blocchi di memoria live
  • potente filtro delle operazioni di memoria basato su heap, tag di memoria, intervallo di tempo, ecc.
  • SDK per strumentazione manuale con codice sorgente completo
  • supporto per l'integrazione continua tramite l'utilizzo della riga di comando
  • chiamare la pila di alberi e la navigazione della mappa ad albero
  • molto di piu.

Gli utenti possono profilare qualsiasi piattaforma di targeting software con cross-compiler GCC o Clang. MTuner viene fornito con supporto integrato per piattaforme Windows, PlayStation 4 e PlayStation 3.


Questa dovrebbe essere la risposta accettata. È un ottimo strumento e può gestire i volumi di allocazioni / deallocazioni che altri non possono.
Serge Rogatch,

3

Su Windows è possibile utilizzare l' heap di debug CRT .

Esistono standard o procedure da seguire per garantire che non vi siano perdite di memoria nel programma.

Sì, non usare la gestione manuale della memoria (se hai mai chiamato deleteo delete[]manualmente, allora stai sbagliando). Utilizza RAII e i puntatori intelligenti, limita le allocazioni di heap al minimo assoluto (la maggior parte delle volte, le variabili automatiche saranno sufficienti).


3

Rispondendo alla seconda parte della tua domanda,

Esistono standard o procedure da seguire per garantire che non vi siano perdite di memoria nel programma.

Si C'è. E questa è una delle differenze chiave tra C e C ++.

In C ++, non dovresti mai chiamare newo deletenel tuo codice utente. RAII è una tecnica molto comunemente usata, che risolve praticamente il problema di gestione delle risorse. Ogni risorsa nel programma (una risorsa è tutto ciò che deve essere acquisito e successivamente rilasciato: handle di file, socket di rete, connessioni al database, ma anche allocazioni di memoria semplici e, in alcuni casi, coppie di chiamate API (BeginX ( ) / EndX (), LockY (), UnlockY ()), dovrebbero essere racchiusi in una classe, dove:

  • il costruttore acquisisce la risorsa (chiamando newse la risorsa è un'allocazione memroy)
  • il distruttore rilascia la risorsa,
  • la copia e l'assegnazione vengono impedite (rendendo privati ​​il ​​costruttore di copie e gli operatori di assegnazione) oppure vengono implementate per funzionare correttamente (ad esempio clonando la risorsa sottostante)

Questa classe viene quindi istanziata localmente, nello stack o come membro della classe e non chiamando newe memorizzando un puntatore.

Spesso non è necessario definire queste classi da soli. Anche i contenitori di librerie standard si comportano in questo modo, in modo che qualsiasi oggetto memorizzato in un oggetto std::vectorvenga liberato quando il vettore viene distrutto. Quindi, di nuovo, non conservare un puntatore nel contenitore (che richiederebbe voi di chiamare newe delete), bensì l'oggetto in sé (che vi dà la gestione della memoria per libero ). Allo stesso modo, le classi di puntatori intelligenti possono essere utilizzate per avvolgere facilmente gli oggetti che devono solo essere allocati newe controllarne la durata.

Ciò significa che quando l'oggetto esce dal campo di applicazione, viene automaticamente distrutto e la sua risorsa rilasciata e ripulita.

Se lo fai in modo coerente in tutto il codice, semplicemente non avrai perdite di memoria. Tutto ciò che potrebbe essere trapelato è legato a un distruttore che è garantito per essere chiamato quando il controllo lascia l'ambito in cui è stato dichiarato l'oggetto.


se il puntatore intelligente contiene una classe e quella classe contiene un puntatore di molte altre classi. quando smart si spegne significa che tutti i puntatori all'interno verranno eliminati in modo sicuro.
Chris_vr,

@Chris: Supponendo che l'oggetto a cui punta il puntatore intelligente ha un distruttore che esegue la pulizia necessaria o l'oggetto contiene membri che hanno essi stessi i distruttori per eseguire la pulizia necessaria. In sostanza, finché ogni oggetto si prende cura di se stesso (ripulendosi dopo se stesso quando viene distrutto), e fino a quando ogni oggetto viene memorizzato per valore, non come un puntatore, allora tutto ciò che deve essere liberato verrà liberato.
jalf

3

AddressSanitizer (ASan) è un rilevatore di errori di memoria veloce. Trova i bug di overflow del buffer {heap, stack, global} use-after-free nei programmi C / C ++. Trova:

  • Usa dopo libero (dereference puntatore penzolante)
  • Overflow del buffer di heap
  • Stack buffer overflow
  • Overflow del buffer globale
  • Utilizzare dopo il ritorno
  • Bug dell'ordine di inizializzazione

Questo strumento è molto veloce. Il rallentamento medio del programma strumentato è ~ 2x.


Vedi in particolare LeakSanitizer
Gabriel Devillers,

0

Oltre agli strumenti e ai metodi forniti nelle altre risposte, è possibile utilizzare strumenti di analisi del codice statico per rilevare perdite di memoria (e anche altri problemi). Uno strumento gratuito e robusto è Cppcheck. Ma ci sono molti altri strumenti disponibili. Wikipedia ha un elenco di strumenti di analisi del codice statico.


-1

Assicurarsi che tutta la memoria dell'heap sia stata liberata correttamente. Non è necessario se non si alloca mai memoria sull'heap. In tal caso, contare il numero di volte in cui si memorizza la memoria e contare il numero di volte in cui si libera memoria.


-3

Né "nuovo" né "elimina" devono mai essere utilizzati nel codice dell'applicazione. Creare invece un nuovo tipo che utilizza il linguaggio manager / worker, in cui la classe manager alloca e libera memoria e inoltra tutte le altre operazioni all'oggetto worker.

Purtroppo questo è più lavoro di quanto dovrebbe essere perché C ++ non ha un sovraccarico di "operatore". È ancora più lavoro in presenza di polimorfismo.

Ma questo vale la pena perché non dovrai mai preoccuparti delle perdite di memoria, il che significa che non devi nemmeno cercarle.

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.