Wie finde ich einen Speicherverlust in einem C ++ - Code / Projekt?


180

Ich bin ein C ++ - Programmierer auf der Windows-Plattform. Ich verwende Visual Studio 2008.

Normalerweise lande ich im Code mit Speicherlecks.

Normalerweise finde ich den Speicherverlust durch Überprüfen des Codes, aber es ist umständlich und nicht immer ein guter Ansatz.

Da ich mir kein kostenpflichtiges Tool zur Erkennung von Speicherlecks leisten kann, wollte ich, dass Sie die bestmöglichen Möglichkeiten zur Vermeidung von Speicherlecks vorschlagen.

  1. Ich möchte wissen, wie der Programmierer Speicherlecks finden kann.
  2. Gibt es einen Standard oder eine Vorgehensweise, die befolgt werden sollte, um sicherzustellen, dass das Programm keinen Speicherverlust aufweist?

29
"Normalerweise lande ich im Code mit Speicherverlust." Wenn Sie automatische Variablen, Container und intelligente Zeiger verwenden (und bewährte Methoden für die Verwendung intelligenter Zeiger befolgen), sollten Speicherverluste äußerst selten sein. Denken Sie daran, dass Sie in fast allen Fällen die automatische Ressourcenverwaltung verwenden sollten .
James McNellis

Dupliziert Probleme, die von mehreren Fragen abgedeckt werden, wie stackoverflow.com/questions/1502799/… und stackoverflow.com/questions/2820223/…
HostileFork sagt, dass Sie SE

1
@ Hostile Fork: "Wie kann man vermeiden, normalerweise in Code mit Speicherlecks zu geraten?" Wird in diesen Antworten nicht behandelt.
Doc Brown

2
@ Doc Brown: Ich hatte nicht das Gefühl, das auch nachzuschlagen, aber es wird alles an anderer Stelle behandelt, wie zum Beispiel stackoverflow.com/questions/45627/…
HostileFork sagt, vertraue SE

1
DIY-Lecksucher: Sie könnten einen verdächtigen Code in eine Endlosschleife einfügen und dann einen Task-Manager öffnen. In der Regel füllt sogar ein kleines Leck den Speicher in Sekunden oder Minuten (dies hängt von Ihrer Codekomplexität und Ihrer CPU ab). Wenn dies nicht der Fall ist, ist dieser Code wahrscheinlich nicht undicht.
Hallo Welt

Antworten:


269

Anleitung

Dinge, die du brauchen wirst

  • Kenntnisse in C ++
  • C ++ - Compiler
  • Debugger und andere Ermittlungssoftware-Tools

1

Verstehen Sie die Grundlagen des Bedieners. Der C ++ - Operator newweist Heapspeicher zu. Der deleteOperator gibt Heapspeicher frei. newVerwenden Sie für jeden einen, deletedamit Sie denselben Speicher freigeben, den Sie zugewiesen haben:

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

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

2

Ordnen Sie den Speicher nur neu zu, wenn Sie ihn gelöscht haben. Erhält im folgenden Code streine neue Adresse mit der zweiten Zuordnung. Die erste Adresse geht unwiederbringlich verloren, ebenso die 30 Bytes, auf die sie zeigte. Jetzt können sie nicht mehr freigegeben werden, und Sie haben ein Speicherleck:

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

Beobachten Sie diese Zeigerzuweisungen. Jede dynamische Variable (zugewiesener Speicher auf dem Heap) muss einem Zeiger zugeordnet werden. Wenn eine dynamische Variable von ihren Zeigern getrennt wird, kann sie nicht mehr gelöscht werden. Dies führt wiederum zu einem Speicherverlust:

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

Seien Sie vorsichtig mit lokalen Zeigern. Ein Zeiger, den Sie in einer Funktion deklarieren, wird auf dem Stapel zugewiesen, aber die dynamische Variable, auf die er zeigt, wird auf dem Heap zugewiesen. Wenn Sie es nicht löschen, bleibt es bestehen, nachdem das Programm die Funktion beendet hat:

void Leak(int x){

char* p = new char [x];

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

}

5

Achten Sie nach "Löschen" auf die eckigen Klammern. Verwenden Sie diese deleteOption, um ein einzelnes Objekt freizugeben. Mit delete []eckigen Klammern verwenden, um ein Heap-Array freizugeben. Mach so etwas nicht:

char* one = new char;

delete [] one; // Wrong

char* many = new char [30];

delete many; // Wrong!

6

Wenn das Leck noch erlaubt ist - ich suche es normalerweise mit Deleaker (überprüfen Sie es hier: http://deleaker.com ).


3
Entschuldigung für Frage-Kommentar, aber was ist mit Funktionsparametern ohne Zeiger? someFunction("some parameter")muss ich "some parameter"in der someFunctionnach dem Funktionsaufruf löschen oder werden diese automatisch gelöscht?
19greg96

1
Vielen Dank für den Link zu Deleaker. Dies ist ein sehr praktisches Tool mit einer übersichtlichen Integration in Visual Studio. Ich könnte viel Zeit damit sparen. wies mich auf die Zeilen hin, in denen ich Speicher zugewiesen und nicht freigegeben habe. Toll. Und es ist billig im Vergleich zu anderen Speicherlecksuchern, die ich gefunden habe.
this.myself

@ John Smith Plz erklären, wie man Fälle ähnlich wie Fall 3 richtig behandelt; str2 = str1; // Schlecht! Jetzt können die 40 Bytes nicht mehr freigegeben werden. wie lösche ich dann str 1 ??
Nihar

1
Was ist, wenn wir Werttypen wie char *, int, float, ... und struct wie Vector, CString verwenden und überhaupt keinen 'neuen' Operator verwenden, wird dies keinen Speicherverlust verursachen, stimmt das?
123iamking

Ich bin nur hier, um zu sagen, dass ich C ++ seit fast 14 Jahren nicht mehr berührt habe ... aber ich bin stolz zu sagen, dass ich dank eines C ++ - Buches, das ich noch besitze und lese, wenn ich es habe, verstanden und mich daran erinnert habe, wie man das alles macht. Ich langweile mich mit c #. Dieses Buch ist Scott Mitchells Effective C ++. Gott, ich habe dieses Buch geliebt. Danke Scott!
JonH

33

Sie können einige Techniken in Ihrem Code verwenden, um Speicherverluste zu erkennen. Die gebräuchlichste und einfachste Methode zum Erkennen besteht darin, ein Makro wie DEBUG_NEW zu definieren und zusammen mit vordefinierten Makros wie __FILE__und __LINE__zum Auffinden des Speicherverlusts in Ihrem Code zu verwenden. Diese vordefinierten Makros geben Ihnen die Datei- und Zeilennummer der Speicherlecks an.

DEBUG_NEW ist nur ein MAKRO, das normalerweise definiert wird als:

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

So dass, wo immer Sie verwenden new , auch die Datei- und Zeilennummer verfolgen, die zum Auffinden von Speicherverlusten in Ihrem Programm verwendet werden kann.

Und __FILE__, __LINE__sind vordefinierte Makros , die jeweils an den Dateinamen und die Zeilennummer auswerten , wo Sie sie verwenden!

Lesen Sie den folgenden Artikel, der die Technik der Verwendung von DEBUG_NEW mit anderen interessanten Makros sehr schön erklärt:

Ein plattformübergreifender Speicherlecksucher


Aus Wikpedia ,

Debug_new bezieht sich auf eine Technik in C ++ zum Überladen und / oder Neudefinieren von Operator new und Operator delete, um die Speicherzuordnungs- und Freigabeaufrufe abzufangen und damit ein Programm für die Speichernutzung zu debuggen. Oft wird ein Makro mit dem Namen DEBUG_NEW definiert, und aus new wird so etwas wie neu (_ FILE _, _) LINE _), um die Datei- / bei der Zuordnung aufzuzeichnen.Microsoft Visual C ++ verwendet diese Technik in seinen Microsoft Foundation-Klassen. Es gibt einige Möglichkeiten, diese Methode zu erweitern, um die Verwendung der Neudefinition von Makros zu vermeiden, während die Datei- / Zeileninformationen auf einigen Plattformen weiterhin angezeigt werden können. Diese Methode weist viele inhärente Einschränkungen auf. Es gilt nur für C ++ und kann keine Speicherverluste durch C-Funktionen wie malloc erkennen. Es kann jedoch sehr einfach zu bedienen und im Vergleich zu einigen vollständigeren Speicher-Debugger-Lösungen auch sehr schnell sein.


4
Dies #defineführt zu einer Überlastung operator newund zu Compilerfehlern. Selbst wenn es Ihnen gelingt, dies zu überwinden, werden die überlasteten Funktionen immer noch nicht angesprochen. Obwohl die Technik gut ist, müssen manchmal viele Codeänderungen vorgenommen werden.
Iammilind

1
@iammilind: Natürlich ist diese Technik keine vollständige Lösung für alle Probleme und sicherlich nicht in allen Situationen anwendbar.
Nawaz

@Chris_vr: auto_ptrfunktioniert nicht mit Standardcontainern wie std::vector, std::listusw. Siehe dazu: stackoverflow.com/questions/111478/...
Nawaz

Okay cool. DATEI und Zeile werden beschrieben. Was ist operator newund welche Versionen davon verwenden Sie?

14

Es gibt einige bekannte Programmiertechniken, mit denen Sie das Risiko von Speicherlecks aus erster Hand minimieren können:

  • Wenn Sie Ihre eigene dynamische Speicherzuweisung vornehmen müssen , schreiben Sie newund deleteimmer paarweise, und stellen Sie sicher, dass der Zuordnungs- / Freigabecode paarweise aufgerufen wird
  • Vermeiden Sie dynamische Speicherzuweisungen, wenn Sie können. Verwenden Sie zum Beispielvector<T> t Sie wo immer möglich anstelle vonT* t = new T[size]
  • Verwenden Sie "intelligente Zeiger" wie Boost-intelligente Zeiger ( http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/smart_ptr.htm ).
  • Mein persönlicher Favorit: Stellen Sie sicher, dass Sie das Konzept des Besitzes eines Zeigers verstanden haben, und stellen Sie sicher, dass Sie überall dort, wo Sie Zeiger verwenden, wissen, welche Codeentität der Eigentümer ist
  • Erfahren Sie, welche Konstruktoren / Zuweisungsoperatoren vom C ++ - Compiler automatisch erstellt werden und was dies bedeutet, wenn Sie eine Klasse haben, die einen Zeiger besitzt (oder was dies bedeutet, wenn Sie eine Klasse haben, die einen Zeiger auf ein Objekt enthält, das sie nicht besitzt).

Ich benutze auto_pointer eines Objekts, das heißt, es löscht alle anderen Klassenobjektzeiger darin.
Chris_vr

@Chris_vr: Wenn Sie eine bestimmte Frage zu auto_pointer haben, würde ich vorschlagen, dass Sie eine neue Frage stellen, einschließlich eines Beispiels.
Doc Brown

Viele Beiträge sagen mir, dass Vektor <> nicht garantiert, dass Speicher beim Löschen freigegeben wird. Ich habe Swap-Sachen usw. persönlich getestet und bin zu dem Schluss gekommen, dass der Vektor <> besonders bei dynamischer Verwendung undicht ist. Ich verstehe nicht, wie Vektor <> über die dynamische Zuweisung zum Selbermachen mit 'neu' und korrekter Bereinigung beraten werden kann. In meinen eingebetteten Programmen vermeide ich die Verwendung von Vektor <> für dynamische Inhalte aufgrund all der Lecks. Dort benutze ich new oder std :: list
bart s

Ich gebe wegen der Anzahl der Zeichen einen zweiten Befehl ein. Leider habe ich in meinem eingebetteten C ++ ein altes C ++ (98?), Das kein shrink_to_fit für einen Vektor hat ... Das eingebettete Programm stürzt jedoch zu 100% ab, wenn der Speicher mit Vektor <> dynamisch ausgeht
bart s


8
  1. Laden Sie die Debugging-Tools für Windows herunter .
  2. Verwenden Sie die gflags Dienstprogramm, um Stapelspuren im Benutzermodus zu aktivieren.
  3. Verwenden Sie UMDHdiese Option , um mehrere Schnappschüsse aus dem Speicher Ihres Programms zu erstellen. Machen Sie einen Snapshot, bevor Speicher zugewiesen wird, und machen Sie einen zweiten Snapshot nach einem Punkt, an dem Sie glauben, dass Ihr Programm Speicher verloren hat. Möglicherweise möchten Sie Ihrem Programm Pausen oder Eingabeaufforderungen hinzufügen, um die Ausführung zu ermöglichenUMDH die Snapshots und erstellen können.
  4. Lauf UMDH erneut aus, diesmal in seinem Modus, der einen Unterschied zwischen den beiden Schnappschüssen macht. Anschließend wird ein Bericht erstellt, der die Anrufstapel vermuteter Speicherlecks enthält.
  5. Stellen Sie Ihre vorherigen gflagsEinstellungen wieder her, wenn Sie fertig sind.

UMDHSie erhalten mehr Informationen als der CRT-Debug-Heap, da er die Speicherzuweisungen während Ihres gesamten Prozesses überwacht. Es kann Ihnen sogar sagen, ob Komponenten von Drittanbietern undicht sind.


1
Ich bevorzuge Deleaker und Valgrind anstelle von Standard-Profiler
z0r1fan

8

Das Ausführen von "Valgrind" kann:

1) Hilfe beim Erkennen von Speicherlecks - Zeigen Sie an, wie viele Speicherlecks Sie haben, und weisen Sie auf die Zeilen im Code hin, in denen der durchgesickerte Speicher zugewiesen wurde.

2) Weisen Sie auf falsche Versuche hin, Speicher freizugeben (z. B. unsachgemäßer Aufruf vondelete ).

Anleitung zur Verwendung von "Valgrind"

1) Holen Sie sich Valgrind hier .

2) Kompilieren Sie Ihren Code mit -g flag

3) In Ihrem Shell-Lauf:

valgrind --leak-check=yes myprog arg1 arg2

Wo "myprog" ist Ihr kompiliertes Programm und arg1,arg2 Ihr Programm Argumente.

4) Das Ergebnis ist eine Liste von Anrufen an malloc/ newdie keine nachfolgenden Anrufe zum freien Löschen hatten.

Beispielsweise:

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

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

Sagt Ihnen, in welcher Zeile die malloc(die nicht befreit wurde) aufgerufen wurde.

Stellen Sie, wie von anderen hervorgehoben, sicher, dass Sie für jeden new/ mallocAnruf einen nachfolgenden delete/ freeAnruf haben.


6

Wenn Sie gcc verwenden, ist gprof verfügbar.

Ich wollte wissen, wie Programmierer Speicherverlust finden

Einige verwenden Tools, andere tun das, was Sie tun, und können dies auch durch Peer-Code-Überprüfung tun

Gibt es einen Standard oder ein Verfahren, das befolgt werden sollte, um sicherzustellen, dass das Programm keinen Speicherverlust aufweist?

Für mich: Wenn ich dynamisch zugewiesene Objekte erstelle, setze ich immer den Freigabecode nach und fülle den Code dazwischen aus. Dies wäre in Ordnung, wenn Sie sicher sind, dass der Code dazwischen keine Ausnahmen enthält. Ansonsten benutze ich try-finally (ich benutze C ++ nicht häufig).


Manchmal können wir die im Konstruktor zugewiesenen Daten nicht löschen. Was ist bei dieser Gelegenheit zu tun?
Chris_vr

5
  1. Im Visual Studio gibt es einen eingebauten Detektor für Speicherverluste namens C Runtime Library. Wenn Ihr Programm nach der Rückkehr der Hauptfunktion beendet wird, überprüft CRT den Debug-Heap Ihrer Anwendung. Wenn auf dem Debug-Heap noch Blöcke zugewiesen sind, liegt ein Speicherverlust vor.

  2. In diesem Forum werden einige Möglichkeiten zur Vermeidung von Speicherverlusten in C / C ++ erläutert.


5

Durchsuchen Sie Ihren Code nach Vorkommen von newund stellen Sie sicher, dass alle in einem Konstruktor mit einem übereinstimmenden Löschvorgang in einem Destruktor vorkommen. Stellen Sie sicher, dass dies die einzige mögliche Auslöseoperation in diesem Konstruktor ist. Eine einfache Möglichkeit, dies zu tun, besteht darin, alle Zeiger in std::auto_ptroder einzuschließen boost::scoped_ptr(je nachdem, ob Sie eine Verschiebungssemantik benötigen oder nicht). Stellen Sie für den gesamten zukünftigen Code nur sicher, dass jede Ressource einem Objekt gehört, das die Ressource in ihrem Destruktor bereinigt. Wenn Sie eine Verschiebungssemantik benötigen, können Sie ein Upgrade auf einen Compiler durchführen, der R-Wert-Referenzen unterstützt (ich glaube, VS2010) und Verschiebungskonstruktoren erstellen. Wenn Sie dies nicht möchten, können Sie eine Vielzahl kniffliger Techniken anwenden, bei denen Swap gewissenhaft eingesetzt wird, oder die Boost.Move-Bibliothek ausprobieren.


Es ist nicht immer möglich, den zugewiesenen Speicher im Konstruktor zu löschen. So gehen Sie mit dieser Situation um
Chris_vr

@Chris_vr Was meinst du? Wenn alle Ihre Zeigerelemente scope_ptrs sind und jedes einzeln initialisiert wird, löschen alle erfolgreich erstellten Zeiger ihre Zeiger, und die anderen halten ohnehin noch keine Zeiger auf den zugewiesenen Speicher. Ich werde in ein paar Stunden ein Beispiel geben, wenn ich von der Arbeit nach Hause komme.
Mankarse

@Chris_vr: Wenn Sie ein bestimmtes Beispiel haben, posten Sie es als neue Frage, damit wir es dort diskutieren können.
Doc Brown

5

Mit dem Tool Valgrind können Sie Speicherlecks erkennen.

Um das Leck in einer bestimmten Funktion zu finden, verwenden Sie am Ende der Funktion exit (0) und führen Sie es dann mit Valgrind aus

`$` valgrind ./your_CPP_program 

5

Eine Übersicht über automatische Speicherleckprüfer

In dieser Antwort vergleiche ich mehrere verschiedene Speicherleckprüfer in einem einfachen, leicht verständlichen Beispiel für Speicherlecks.

Sehen Sie sich vor allem diese riesige Tabelle im ASan-Wiki an, in der alle dem Menschen bekannten Tools verglichen werden: https://github.com/google/sanitizers/wiki/AddressSanitizerComparisonOfMemoryTools/d06210f759fec97066888e5f27c7e722832b0924

Das analysierte Beispiel lautet:

Haupt 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 stromaufwärts .

Wir werden versuchen zu sehen, wie deutlich die verschiedenen Tools uns auf die undichten Anrufe hinweisen.

tcmalloc von gperftools von Google

https://github.com/gperftools/gperftools

Verwendung unter 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

Die Ausgabe des Programmlaufs enthält die Speicherverlustanalyse:

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

und die Ausgabe von google-pprofenthält die Heap-Nutzungsanalyse:

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

Die Ausgabe weist uns auf zwei der drei Lecks hin:

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

Ich bin mir nicht sicher, warum der dritte nicht aufgetaucht ist

Auf jeden Fall passiert es oft, wenn etwas undicht ist, und wenn ich es für ein reales Projekt verwendet habe, wurde ich einfach sehr leicht auf die undichte Funktion hingewiesen.

Wie in der Ausgabe selbst erwähnt, führt dies zu einer erheblichen Verlangsamung der Ausführung.

Weitere Dokumentation unter:

Siehe auch: Wie verwende ich TCMalloc?

Getestet in Ubuntu 19.04, Google-Perftools 2.5-2.

Address Sanitizer (ASan) auch von Google

https://github.com/google/sanitizers

Zuvor erwähnt unter: Wie finde ich einen Speicherverlust in einem C ++ - Code / Projekt?TODO gegen tcmalloc.

Dies ist bereits in GCC integriert, sodass Sie einfach Folgendes tun können:

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

und Ausführungsausgaben:

=================================================================
==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).

das identifiziert alle Lecks eindeutig. Nett!

ASan kann auch andere coole Überprüfungen durchführen, z. B. Schreibvorgänge außerhalb der Grenzen: Stapelzerstörung erkannt

Getestet in Ubuntu 19.04, GCC 8.3.0.

Valgrind

http://www.valgrind.org/

Zuvor erwähnt unter: https://stackoverflow.com/a/37661630/895245

Verwendung:

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

Ausgabe:

==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)

Es wurden also wieder alle Lecks festgestellt.

Siehe auch: Wie verwende ich Valgrind, um Speicherlecks zu finden?

Getestet in Ubuntu 19.04, valgrind 3.14.0.


4

Visual Leak Detector (VLD) ist ein kostenloses, robustes Open-Source-System zur Erkennung von Speicherlecks für Visual C ++.

Wenn Sie Ihr Programm unter dem Visual Studio-Debugger ausführen, gibt Visual Leak Detector am Ende Ihrer Debugging-Sitzung einen Speicherverlustbericht aus. Der Leckbericht enthält den vollständigen Aufrufstapel, der zeigt, wie durchgesickerte Speicherblöcke zugewiesen wurden. Doppelklicken Sie auf eine Zeile im Aufrufstapel, um zu dieser Datei und Zeile im Editorfenster zu springen.

Wenn Sie nur Absturzabbilder haben, können Sie den !heap -lBefehl Windbg verwenden , der durchgesickerte Heap-Blöcke erkennt . Öffnen Sie besser die Option gflags: "Benutzermodus-Stack-Trace-Datenbank erstellen", dann wird der Aufrufstapel für die Speicherzuordnung angezeigt.


4

MTuner ist ein kostenloses Tool zur Speicherprofilerstellung, Leckerkennung und -analyse auf mehreren Plattformen, das MSVC-, GCC- und Clang-Compiler unterstützt. Features sind:

  • Zeitleistenbasierter Verlauf der Speichernutzung und Live-Speicherblöcke
  • Leistungsstarke Filterung von Speicheroperationen basierend auf Heap, Speicher-Tag, Zeitbereich usw.
  • SDK für manuelle Instrumentierung mit vollständigem Quellcode
  • Kontinuierliche Integrationsunterstützung durch Befehlszeilennutzung
  • Call Stack Tree und Tree Map Navigation
  • viel mehr.

Benutzer können alle Software-Targeting-Plattformen mit GCC- oder Clang-Cross-Compilern profilieren. MTuner bietet integrierte Unterstützung für Windows-, PlayStation 4- und PlayStation 3-Plattformen.


Dies sollte die akzeptierte Antwort sein. Es ist ein großartiges Tool und kann die Volumina von Zuweisungen / Freigaben verarbeiten, die andere nicht können.
Serge Rogatch

3

Unter Windows können Sie den CRT-Debug-Heap verwenden .

Gibt es einen Standard oder ein Verfahren, das befolgt werden sollte, um sicherzustellen, dass das Programm keinen Speicherverlust aufweist?

Ja, verwenden Sie keine manuelle Speicherverwaltung (wenn Sie jemals anrufen deleteoder delete[]manuell, dann machen Sie es falsch). Verwenden Sie RAII- und Smart-Zeiger, und beschränken Sie die Heap-Zuweisungen auf das absolute Minimum (meistens reichen automatische Variablen aus).


3

Beantwortung des zweiten Teils Ihrer Frage,

Gibt es einen Standard oder ein Verfahren, das befolgt werden sollte, um sicherzustellen, dass das Programm keinen Speicherverlust aufweist?

Ja da ist. Und dies ist einer der Hauptunterschiede zwischen C und C ++.

In C ++ sollten Sie niemals newoder deletein Ihrem Benutzercode aufrufen . RAII ist eine sehr häufig verwendete Technik, die das Problem der Ressourcenverwaltung weitgehend löst. Jede Ressource in Ihrem Programm (eine Ressource ist alles, was erworben und später freigegeben werden muss: Dateihandles, Netzwerk-Sockets, Datenbankverbindungen, aber auch einfache Speicherzuordnungen und in einigen Fällen Paare von API-Aufrufen (BeginX ( ) / EndX (), LockY (), UnlockY ()) sollten in eine Klasse eingeschlossen werden, wobei:

  • Der Konstruktor erfasst die Ressource (durch Aufrufen, newwenn es sich bei der Ressource um eine Memroy-Zuordnung handelt).
  • der Destruktor gibt die Ressource frei,
  • Das Kopieren und Zuweisen wird entweder verhindert (indem der Kopierkonstruktor und die Zuweisungsoperatoren privat gemacht werden) oder so implementiert, dass sie ordnungsgemäß funktionieren (z. B. durch Klonen der zugrunde liegenden Ressource).

Diese Klasse wird dann lokal, auf dem Stapel oder als Klassenmitglied instanziiert und nicht durch Aufrufennew und Speichern eines Zeigers.

Sie müssen diese Klassen oft nicht selbst definieren. Die Standardbibliothekscontainer verhalten sich ebenfalls so, sodass jedes in a gespeicherte Objekt std::vectorfreigegeben wird, wenn der Vektor zerstört wird. Speichern Sie also keinen Zeiger im Container (für den Sie aufrufen müssten newund delete), sondern das Objekt selbst (mit dem Sie kostenlos Speicher verwalten können ). Ebenso können intelligente Zeigerklassen verwendet werden, um Objekte, denen nur zugewiesen werden muss, einfach zu verpacken newund ihre Lebensdauer zu steuern.

Dies bedeutet, dass das Objekt, wenn es den Gültigkeitsbereich verlässt, automatisch zerstört und seine Ressource freigegeben und bereinigt wird.

Wenn Sie dies im gesamten Code konsistent tun, treten einfach keine Speicherlecks auf. Alles, was durchgesickert sein könnte , ist an einen Destruktor gebunden, der garantiert aufgerufen wird, wenn die Steuerung den Bereich verlässt, in dem das Objekt deklariert wurde.


Wenn der intelligente Zeiger eine Klasse enthält und diese Klasse einen Zeiger mehrerer anderer Klassen enthält. Wenn smart ausgeht, bedeutet dies, dass der gesamte Zeiger im Inneren sicher gelöscht wird.
Chris_vr

@Chris: Angenommen , das Objekt, auf das der Smart Pointer zeigt, verfügt über einen Destruktor, der die erforderliche Bereinigung durchführt, oder das Objekt enthält Elemente, die selbst Destruktoren haben, um die erforderliche Bereinigung durchzuführen. Im Wesentlichen wird alles, was freigegeben werden muss , freigegeben , solange sich jedes Objekt um sich selbst kümmert (wenn es zerstört wird) und solange jedes Objekt nach Wert und nicht als Zeiger gespeichert wird .
Jalf

3

AddressSanitizer (ASan) ist ein schneller Speicherfehlerdetektor. In C / C ++ - Programmen werden Use-After-Free- und {Heap, Stack, Global} -Puffer-Überlauffehler gefunden. Es findet:

  • Verwenden Sie nach frei (baumelnde Zeiger Dereferenzierung)
  • Heap-Pufferüberlauf
  • Stapelpufferüberlauf
  • Globaler Pufferüberlauf
  • Nach Rückgabe verwenden
  • Fehler bei der Initialisierungsreihenfolge

Dieses Tool ist sehr schnell. Die durchschnittliche Verlangsamung des instrumentierten Programms beträgt ~ 2x.


Siehe insbesondere LeakSanitizer
Gabriel Devillers

0

Zusätzlich zu den in den anderen Antworten bereitgestellten Tools und Methoden können statische Code-Analyse-Tools verwendet werden, um Speicherlecks (und andere Probleme) zu erkennen. Ein kostenloses und robustes Tool ist Cppcheck. Es stehen jedoch noch viele andere Tools zur Verfügung. Wikipedia verfügt über eine Liste statischer Code-Analyse-Tools.


-1

Stellen Sie sicher, dass der gesamte Heapspeicher erfolgreich freigegeben wurde. Es ist nicht erforderlich, wenn Sie dem Heap niemals Speicher zuweisen. Wenn Sie dies tun, zählen Sie, wie oft Sie Speicher speichern und wie oft Sie Speicher freigeben.


-3

Weder "neu" noch "löschen" sollte jemals im Anwendungscode verwendet werden. Erstellen Sie stattdessen einen neuen Typ, der die Manager- / Worker-Sprache verwendet, in der die Manager-Klasse Speicher zuweist und freigibt und alle anderen Vorgänge an das Worker-Objekt weiterleitet.

Leider ist dies mehr Arbeit als es sein sollte, da C ++ keine Überladung von "Operator" hat. Es ist noch mehr Arbeit in Gegenwart von Polymorphismus.

Dies ist jedoch die Mühe wert, da Sie sich dann nie um Speicherlecks sorgen müssen, was bedeutet, dass Sie nicht einmal danach suchen müssen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.