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.
- Ich möchte wissen, wie der Programmierer Speicherlecks finden kann.
- Gibt es einen Standard oder eine Vorgehensweise, die befolgt werden sollte, um sicherzustellen, dass das Programm keinen Speicherverlust aufweist?
c++
memory-leaks
Chris_vr
quelle
quelle
Antworten:
Anleitung
Dinge, die du brauchen wirst
1
Verstehen Sie die Grundlagen des Bedieners. Der C ++ - Operator
new
weist Heapspeicher zu. Derdelete
Operator gibt Heapspeicher frei.new
Verwenden Sie für jeden einen,delete
damit Sie denselben Speicher freigeben, den Sie zugewiesen haben:2
Ordnen Sie den Speicher nur neu zu, wenn Sie ihn gelöscht haben. Erhält im folgenden Code
str
eine 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: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:
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:
5
Achten Sie nach "Löschen" auf die eckigen Klammern. Verwenden Sie diese
delete
Option, um ein einzelnes Objekt freizugeben. Mitdelete []
eckigen Klammern verwenden, um ein Heap-Array freizugeben. Mach so etwas nicht:6
Wenn das Leck noch erlaubt ist - ich suche es normalerweise mit Deleaker (überprüfen Sie es hier: http://deleaker.com ).
quelle
someFunction("some parameter")
muss ich"some parameter"
in dersomeFunction
nach dem Funktionsaufruf löschen oder werden diese automatisch gelöscht?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:
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 ,
quelle
#define
führt zu einer Überlastungoperator new
und 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.auto_ptr
funktioniert nicht mit Standardcontainern wiestd::vector
,std::list
usw. Siehe dazu: stackoverflow.com/questions/111478/...operator new
und welche Versionen davon verwenden Sie?Es gibt einige bekannte Programmiertechniken, mit denen Sie das Risiko von Speicherlecks aus erster Hand minimieren können:
new
unddelete
immer paarweise, und stellen Sie sicher, dass der Zuordnungs- / Freigabecode paarweise aufgerufen wirdvector<T> t
Sie wo immer möglich anstelle vonT* t = new T[size]
quelle
Valgrind http://valgrind.org/
und
GDB http://www.gnu.org/software/gdb/
quelle
gflags
Dienstprogramm, um Stapelspuren im Benutzermodus zu aktivieren.UMDH
diese 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.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.gflags
Einstellungen wieder her, wenn Sie fertig sind.UMDH
Sie 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.quelle
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 von
delete
).Anleitung zur Verwendung von "Valgrind"
1) Holen Sie sich Valgrind hier .
2) Kompilieren Sie Ihren Code mit
-g
flag3) In Ihrem Shell-Lauf:
Wo "myprog" ist Ihr kompiliertes Programm und
arg1
,arg2
Ihr Programm Argumente.4) Das Ergebnis ist eine Liste von Anrufen an
malloc
/new
die keine nachfolgenden Anrufe zum freien Löschen hatten.Beispielsweise:
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
/malloc
Anruf einen nachfolgendendelete
/free
Anruf haben.quelle
Wenn Sie gcc verwenden, ist gprof verfügbar.
Einige verwenden Tools, andere tun das, was Sie tun, und können dies auch durch Peer-Code-Überprüfung tun
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).
quelle
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.
In diesem Forum werden einige Möglichkeiten zur Vermeidung von Speicherverlusten in C / C ++ erläutert.
quelle
Durchsuchen Sie Ihren Code nach Vorkommen von
new
und 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 instd::auto_ptr
oder einzuschließenboost::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.quelle
scope_ptr
s 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.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
quelle
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
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:
Die Ausgabe des Programmlaufs enthält die Speicherverlustanalyse:
und die Ausgabe von
google-pprof
enthält die Heap-Nutzungsanalyse:Die Ausgabe weist uns auf zwei der drei Lecks hin:
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:
und Ausführungsausgaben:
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:
Ausgabe:
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.
quelle
Visual Leak Detector (VLD) ist ein kostenloses, robustes Open-Source-System zur Erkennung von Speicherlecks für Visual C ++.
Wenn Sie nur Absturzabbilder haben, können Sie den
!heap -l
Befehl 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.quelle
MTuner ist ein kostenloses Tool zur Speicherprofilerstellung, Leckerkennung und -analyse auf mehreren Plattformen, das MSVC-, GCC- und Clang-Compiler unterstützt. Features sind:
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.
quelle
Unter Windows können Sie den CRT-Debug-Heap verwenden .
Ja, verwenden Sie keine manuelle Speicherverwaltung (wenn Sie jemals anrufen
delete
oderdelete[]
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).quelle
Beantwortung des zweiten Teils Ihrer Frage,
Ja da ist. Und dies ist einer der Hauptunterschiede zwischen C und C ++.
In C ++ sollten Sie niemals
new
oderdelete
in 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:new
wenn es sich bei der Ressource um eine Memroy-Zuordnung handelt).Diese Klasse wird dann lokal, auf dem Stapel oder als Klassenmitglied instanziiert und nicht durch Aufrufen
new
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::vector
freigegeben wird, wenn der Vektor zerstört wird. Speichern Sie also keinen Zeiger im Container (für den Sie aufrufen müsstennew
unddelete
), 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 verpackennew
und 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.
quelle
AddressSanitizer (ASan) ist ein schneller Speicherfehlerdetektor. In C / C ++ - Programmen werden Use-After-Free- und {Heap, Stack, Global} -Puffer-Überlauffehler gefunden. Es findet:
Dieses Tool ist sehr schnell. Die durchschnittliche Verlangsamung des instrumentierten Programms beträgt ~ 2x.
quelle
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.
quelle
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.
quelle
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.
quelle