Heim >Backend-Entwicklung >C#.Net-Tutorial >Erkennung von Speicherlecks in C++

Erkennung von Speicherlecks in C++

巴扎黑
巴扎黑Original
2016-11-30 11:24:501235Durchsuche

Zuerst müssen wir wissen, ob das Programm einen Speicherverlust aufweist, und dann herausfinden, welche Codezeile den Speicherverlust aufweist, damit wir ihn beheben können.

Der einfachste Weg ist natürlich die Verwendung professioneller Erkennungstools wie BoundsCheck, das bekannt und sehr leistungsfähig ist. Ich glaube, dass niemand, der C++ entwickelt, darauf verzichten kann. Darüber hinaus
Zuerst müssen wir wissen, ob das Programm einen Speicherverlust aufweist, und dann ermitteln, welche Codezeile den Speicherverlust aufweist, damit wir ihn beheben können.

Der einfachste Weg ist natürlich die Verwendung professioneller Erkennungstools wie BoundsCheck, das bekannt und sehr leistungsfähig ist. Ich glaube, dass niemand, der C++ entwickelt, darauf verzichten kann. Darüber hinaus verwenden Sie keine Tools, sondern implementieren die Überwachung von Speicherlecks selbst, die in die folgenden zwei Situationen unterteilt werden kann:

1. Erkennen von Speicherlecks in MFC

Wenn Sie Sie verwenden ein MFC-Programm. Wenn ja, ist es ganz einfach. Standardmäßig gibt es eine Funktion zur Erkennung von Speicherlecks.

Wir haben VS2005 zum Generieren eines MFC-Dialogfeldprogramms verwendet und festgestellt, dass es Speicherlecks automatisch erkennen kann. Nach sorgfältiger Beobachtung haben wir festgestellt, dass dies in jeder CPP-Datei der Fall ist sind der folgende Code:
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

DEBUG_NEW Dieses Makro ist in der Datei afx.h definiert, was uns hilft, Speicherlecks zu lokalisieren.

Wenn der Speicher nicht gelöscht wird, nachdem Speicher in der CPP-Datei mit dem obigen Code zugewiesen wurde, zeigt das Ausgabefenster von VisualStudio beim Stoppen des Programms die folgenden Informationen an:
Erkannte Speicherlecks!
Objekte sichern ->
d:codemfctestmfctest.cpp(80): {157} normaler Block bei 0x003AF170, 4 Bytes lang.
Daten: < > .

Doppelklicken Sie auf die fett gedruckte Linie im Ausgabefenster. Die IDE öffnet die Datei und findet die Zeile. Es ist leicht zu erkennen, wo der Speicherverlust auftritt.

2. Erkennen von Speicherlecks in reinen C++-Programmen

Ich habe die mit Visual Studio erstellten Win32-Konsolenanwendungs- und Win32-Projektprojekte ausprobiert, aber die Speicherlecks konnten nicht erkannt werden.

Im Folgenden finden Sie einen schrittweisen Prozess zum Einrichten des Speicherleckerkennungsmechanismus des Programms.

Zunächst müssen wir wissen, dass die Debug-Version der C-Laufzeitbibliothek viele Erkennungsfunktionen bietet, die uns das Debuggen des Programms erleichtern. Hierzu gibt es in MSDN ein spezielles Kapitel mit dem Titel „Debug-Routinen“. Ich empfehle Ihnen, zuerst den Inhalt zu lesen.

Wir werden darin mehrere wichtige Funktionen nutzen. Das wichtigste ist _CrtDumpMemoryLeaks; sehen Sie sich die Hilfe in MSDN selbst an. Um diese Funktion verwenden zu können, müssen Sie die Header-Datei crtdbg.h einbinden.

Diese Funktion ist nur in der Debug-Version nützlich. Wenn das Programm unter dem Debugger ausgeführt wird, zeigt _CrtDumpMemoryLeaks Informationen zu Speicherlecks in der „Ausgabe“ an. Fenster .Schreiben Sie einen Code, um ihn wie folgt zu testen:

Speicherleck Version eins erkennen:

#include "stdafx.h"
#include
int _tmain(int argc, _TCHAR* argv[])
{
int* p = new int;
_CrtDumpMemoryLeaks;
return 0;
}

Nach dem Ausführen, im Ausgabefenster (Ausgabe) mit folgenden Informationen:

Erkannte Speicherlecks!
Objekte werden ausgegeben ->
{112} normaler Block bei 0x003AA770, 4 Bytes lang.
Daten: < > ; 00 00 00 00
Objekt-Dump abgeschlossen.

Aber das zeigt uns nur, dass das Programm einen Speicherverlust aufweist, und es ist auf den ersten Blick schwer zu erkennen, wo dieser Speicherverlust vorliegt.

Sehen Sie sich unsere Speicherleckerkennung Version 2 an:

#include "stdafx.h"

#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
int _tmain(int argc, _TCHAR* argv[])
{
int* p = new int;
_CrtDumpMemoryLeaks;
return 0;
}

The The Das Programm definiert mehrere Makros und verwendet Makros, um new in der Debug-Version zu ersetzen. Beim Aufruf von new werden die folgenden Ergebnisse angezeigt:

Erkannte Speicherlecks!

Objekte ausgeben ->
d:codeconsoletestconsoletest.cpp(21): {112} Client-Block bei 0x003A38B0, Subtyp 0, 4 Bytes lang.
Daten: < 00 00 00
Objekt-Dump abgeschlossen.

Haha, es hat den gleichen Effekt wie das MFC-Programm, aber warten Sie eine Minute. Schauen Sie sich den folgenden Code an:

int _tmain(int argc, _TCHAR* argv[])

{
int* p = new int;
_CrtDumpMemoryLeaks;
delete p ;
return 0;
}

Nachdem wir ausgeführt haben, können wir feststellen, dass wir den Zeiger gelöscht haben, aber dennoch ein Speicherleck gemeldet wurde. Man kann sich also vorstellen, dass jedes Mal, wenn new aufgerufen wird, der Aufruf intern im Programm aufgezeichnet wird, ähnlich wie bei einem Array-Datensatz. Wenn er gelöscht wird, wird er aus dem Array gelöscht und _CrtDumpMemoryLeaks gibt den aktuellen Status aus des Arrays.

Zusätzlich zum Speichern von Speicherinformationen bei Bedarf ist es am wichtigsten, _CrtDumpMemoryLeaks einmal zu verwenden, wenn das Programm beendet wird.

Wenn das Programm mehr als einen Exit hat, benötigen wir diese Funktion an mehreren Orten aufgerufen werden.

Was passiert außerdem, wenn das Programm den Zeiger im Destruktor der Klasse löscht? Zum Beispiel:

#include "stdafx.h"
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
# _CRTDBG_MAP_ALLOC
#include
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
class Test
{
public:
Test define { _p = new int;🎜>~Test { delete _p;
};
int _tmain(int argc, _TCHAR* argv[])
{
int* p = new int;
delete p;
Test t;
_CrtDumpMemoryLeaks;
return 0;
}

Sie können sehen, dass der Destruktor beendet wird, wenn das Programm ausgeführt wird Exits Es wird nur aufgerufen, wenn es aufgerufen wird. Es liegt offensichtlich kein Speicherverlust vor, aber diese Schreibweise wird trotzdem gemeldet.

Informationen zur Verbesserung finden Sie in Version 3 der Speicherleckerkennung:

#include "stdafx.h"

#ifdef _DEBUG

#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
# endif
class Test
{
public:
Test { _p = new int; }
~Test { delete _p; }
int* _p;
};
int _tmain(int argc, _TCHAR* argv[])
{
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
int* p = new int;
delete p;
Test t;
return 0;
}

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); Diese Anweisung ruft automatisch _CrtDumpMemoryLeaks auf, wenn das Programm beendet wird. _CRTDBG_ALLOC_MEM_DF und _CRTDBG_LEAK_CHECK_DF müssen gleichzeitig festgelegt werden.

Auf diese Weise hat diese Version den gleichen Effekt wie MFC erzielt, aber ich denke, das reicht nicht aus, da wir nur Informationen im Ausgabefenster ausgeben, was ist für Entwickler sehr wichtig. Die Erinnerung ist noch nicht offensichtlich und wird oft übersehen. Darüber hinaus ist es schwierig, es zu reparieren, selbst wenn viele Leute es entdecken, und beeinträchtigt die externe Leistung des Programms nicht ernsthaft. Wie können Entwickler Speicherlecks proaktiv beheben? Ich erinnere mich, dass ich einmal mit jemandem zusammengearbeitet habe, um ein Programm zu schreiben. Meine Funktionsparameter hatten Anforderungen und durften nicht leer sein, aber andere hatten immer keine andere Wahl, als die Funktionsparameter am Anfang der Funktion zu überprüfen und zu bestätigen Auf diese Weise tauchte beim Ausführen des Programms immer wieder die Behauptung auf, was das Debuggen des Programms sehr anstrengend machte. Schließlich waren andere Programmierer verärgert, sodass sie das Problem behoben und die Parameter korrekt eingegeben hatten. Ich denke also, wenn wir wollen, dass Programmierer die Initiative ergreifen, etwas zu tun, müssen wir ihnen zunächst das Gefühl geben, dass dies ihre Belastung verringern und ihre Arbeit erleichtern kann. Haha, machen wir das Gleiche. Wenn das Programm beendet wird und ein Speicherverlust erkannt wird, wird das Programm Sie dazu auffordern.

Siehe Version 4 zur Erkennung von Speicherlecks:

#include "stdafx.h"

#include

#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK new ( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include
#ifdef _DEBUG
# new DEBUG_CLIENTBLOCK
#endif
void Exit
{
int i = _CrtDumpMemoryLeaks;
assert( i == 0);
}
int _tmain(int argc, _TCHAR define * argv[])
{
atexit(Exit);
int* p = new int;
return 0;
}

Diese Version wird ausgeführt, wenn Das Programm wird beendet. Wenn ein Speicherverlust vorliegt, wird ein Eingabeaufforderungsdialogfeld angezeigt.

atexit(Exit) legt die Exit-Funktion fest, die beim Beenden des Programms ausgeführt werden soll. Wenn in der Exit-Funktion ein Speicherverlust vorliegt, gibt _CrtDumpMemoryLeaks einen Wert ungleich 0 zurück und dieser wird bestätigt.

Diese Version ist einsatzbereit. Aber wir können noch einige Verbesserungen vornehmen, denn wenn wir wirklich alle Speicherlecks im Code genau erkennen wollen, müssen wir das #define... im Code mit new in alle Dateien kopieren. Es ist unmöglich, so viel Code für jede Datei zu kopieren, also können wir ihn extrahieren und in eine Datei einfügen. Der Inhalt der Datei lautet wie folgt:

#pragma Once

#ifdef _DEBUG

#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
# einschließen < ;stdlib. h>
#include
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif

Dann fügen Sie KDetectMemoryLeak.h in die gemeinsame Datei ein des Projekts. Beispielsweise wird ein mit VS erstelltes Projekt es in stdafx.h einschließen. Oder ich habe selbst eine Common.h-Datei erstellt, die allgemeinen Code enthält, der von praktisch allen Dateien verwendet wird.

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn