Finden von Arbeitsspeicherverlusten mit der CRT-BibliothekFind memory leaks with the CRT library

Arbeitsspeicherverluste gehören zu den subtilsten und am schwersten zu erkennenden Fehlern in C/C++-Apps.Memory leaks are among the most subtle and hard-to-detect bugs in C/C++ apps. Arbeitsspeicherverluste entstehen dadurch, dass zuvor zugewiesener Arbeitsspeicher nicht ordnungsgemäß freigegeben wird.Memory leaks result from the failure to correctly deallocate memory that was previously allocated. Ein geringfügiger Arbeitsspeicherverlust wird möglicherweise zunächst nicht bemerkt, aber im Laufe der Zeit kann es zu Symptomen von einer schlechten Leistung bis hin zu Abstürzen kommen, wenn die App nicht mehr über genügend Arbeitsspeicher verfügt.A small memory leak might not be noticed at first, but over time can cause symptoms ranging from poor performance to crashing when the app runs out of memory. Eine App mit einem Speicherverlust, der den gesamten verfügbaren Arbeitsspeicher betrifft, kann dazu führen, dass andere Apps abstürzen, wodurch Unsicherheit entsteht, welche App für das Problem verantwortlich ist.A leaking app that uses up all available memory can cause other apps to crash, creating confusion as to which app is responsible. Selbst harmlose Arbeitsspeicherverluste können auf andere Probleme hindeuten, die korrigiert werden sollten.Even harmless memory leaks might indicate other problems that should be corrected.

Der Visual StudioVisual Studio-Debugger und die C-Laufzeitbibliothek (CRT) können Ihnen helfen, Arbeitsspeicherverluste zu erkennen und zu identifizieren.The Visual StudioVisual Studio debugger and C Run-time Library (CRT) can help you detect and identify memory leaks.

Aktivieren der ArbeitsspeicherverlusterkennungEnable memory leak detection

Die wichtigsten Tools zum Erkennen von Arbeitsspeicherverlusten sind der C/C++-Debugger und die Debugging-Heapfunktionen der C-Laufzeitbibliothek (CRT).The primary tools for detecting memory leaks are the C/C++ debugger and the C Run-time Library (CRT) debug heap functions.

Um alle Debugging-Heapfunktionen zu aktivieren, fügen Sie die folgenden Anweisungen in der hier genannten Reihenfolge in das C++-Programm ein:To enable all the debug heap functions, include the following statements in your C++ program, in the following order:

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

Durch die #define -Anweisung wird eine Basisversion der CRT-Heapfunktionen der entsprechenden Debugversion zugeordnet.The #define statement maps a base version of the CRT heap functions to the corresponding debug version. Wenn Sie die #define-Anweisung weglassen, ist das Speicherabbild des Arbeitsspeicherverlusts weniger detailliert.If you leave out the #define statement, the memory leak dump will be less detailed.

Durch das Einschließen von crtdbg.h werden die Funktionen malloc und free den zugehörigen Debugversionen _malloc_dbg und _free_dbg zugeordnet, welche die Speicherbelegung und -freigabe nachverfolgen.Including crtdbg.h maps the malloc and free functions to their debug versions, _malloc_dbg and _free_dbg, which track memory allocation and deallocation. Diese Zuordnung findet nur in Debugbuilds mit _DEBUGstatt.This mapping occurs only in debug builds, which have _DEBUG. Releasebuilds verwenden die normalen Funktionen malloc und free .Release builds use the ordinary malloc and free functions.

Nachdem Sie die Debugging-Heapfunktionen mithilfe der vorangehenden-Anweisungen aktiviert haben, platzieren Sie einen Aufruf von _CrtDumpMemoryLeaks vor einem Endpunkt der App, sodass beim Beenden der App ein Bericht über Arbeitsspeicherverluste angezeigt wird.After you've enabled the debug heap functions by using the preceding statements, place a call to _CrtDumpMemoryLeaks before an app exit point to display a memory-leak report when the app exits.

_CrtDumpMemoryLeaks();

Wenn Ihre App mehrere Endpunkte aufweist, müssen Sie _CrtDumpMemoryLeaks nicht manuell an jedem Endpunkt platzieren.If your app has several exits, you don't need to manually place _CrtDumpMemoryLeaks at every exit point. Um einen automatischen Aufruf von _CrtDumpMemoryLeaks an jedem Endpunkt auszulösen, platzieren Sie am Anfang Ihrer App einen Aufruf von _CrtSetDbgFlag mit den hier gezeigten Bitfeldern:To cause an automatic call to _CrtDumpMemoryLeaks at each exit point, place a call to _CrtSetDbgFlag at the beginning of your app with the bit fields shown here:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

Standardmäßig gibt _CrtDumpMemoryLeaks den Arbeitsspeicherverlust-Bericht im Bereich Debuggen des Ausgabefensters aus.By default, _CrtDumpMemoryLeaks outputs the memory-leak report to the Debug pane of the Output window. Bei Verwendung einer Bibliothek wird die Ausgabe u. U. auf einen anderen Ort zurückgesetzt.If you use a library, the library might reset the output to another location.

Sie können _CrtSetReportMode verwenden, um den Bericht an einen anderen Speicherort oder wieder an das Fenster Ausgabe umzuleiten – wie hier gezeigt:You can use _CrtSetReportMode to redirect the report to another location, or back to the Output window as shown here:

_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );

Interpretieren des Arbeitsspeicherverlust-BerichtsInterpret the memory-leak report

Wenn _CRTDBG_MAP_ALLOC nicht in der App definiert ist, zeigt _CrtDumpMemoryLeaks einen Arbeitsspeicherverlust-Bericht an, der wie folgt aussieht:If your app doesn't define _CRTDBG_MAP_ALLOC, _CrtDumpMemoryLeaks displays a memory-leak report that looks like:

Detected memory leaks!
Dumping objects ->
{18} normal block at 0x00780E80, 64 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

Wenn _CRTDBG_MAP_ALLOC in der App definiert ist, sieht der Arbeitsspeicherverlust-Bericht wie folgt aus:If your app defines _CRTDBG_MAP_ALLOC, the memory-leak report looks like:

Detected memory leaks!
Dumping objects ->
c:\users\username\documents\projects\leaktest\leaktest.cpp(20) : {18}
normal block at 0x00780E80, 64 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

Der zweite Bericht enthält den Dateinamen und die Zeilennummer, in der der Speicherblock mit dem Arbeitsspeicherverlust zuerst belegt wurde.The second report shows the filename and line number where the leaked memory is first allocated.

Unabhängig davon, ob Sie _CRTDBG_MAP_ALLOC definieren oder nicht, sind im Arbeitsspeicherverlust-Bericht folgende Informationen enthalten:Whether or not you define _CRTDBG_MAP_ALLOC, the memory-leak report displays:

  • Die Speicherbelegungsnummer, in diesem Beispiel 18The memory allocation number, which is 18 in the example
  • Der Blocktyp, in diesem Beispiel normalThe block type, normal in the example.
  • Der hexadezimale Speicherbereich, in diesem Beispiel 0x00780E80The hexadecimal memory location, 0x00780E80 in the example.
  • Die Größe des Blocks, in diesem Beispiel 64 bytesThe size of the block, 64 bytes in the example.
  • Die ersten 16 Bytes Daten im Block im HexadezimalformatThe first 16 bytes of data in the block, in hexadecimal form.

Speicherblocktypen sind normal, Client oder CRT.Memory block types are normal, client, or CRT. Ein normaler Block ist gewöhnlicher, durch das Programm belegter Speicher.A normal block is ordinary memory allocated by your program. Ein Clientblock ist ein spezieller Speicherblocktyp, der von MFC-Programmen für Objekte verwendet wird, die einen Destruktor erfordern.A client block is a special type of memory block used by MFC programs for objects that require a destructor. Durch den MFC-Operator new wird entsprechend dem zu erstellenden Objekt ein normaler Block oder ein Clientblock erstellt.The MFC new operator creates either a normal block or a client block, as appropriate for the object being created.

Ein CRT-Block wird von der CRT-Bibliothek zur eigenen Verwendung belegt.A CRT block is allocated by the CRT library for its own use. Die CRT-Bibliothek behandelt das Aufheben der Zuordnung für diese Blöcke, sodass CRT-Blöcke nicht im Arbeitsspeicherverlust-Bericht angezeigt werden, es sei denn, es handelt sich um schwerwiegende Probleme mit der CRT-Bibliothek.The CRT library handles the deallocation for these blocks, so CRT blocks won't appear in the memory-leak report unless there are serious problems with the CRT library.

Zwei weitere Speicherblocktypen werden nie in Arbeitsspeicherverlust-Berichten angezeigt.There are two other types of memory blocks that never appear in memory-leak reports. Bei einem freien Block handelt es sich um Arbeitsspeicher, der freigegeben wurde, sodass es sich definitionsgemäß nicht um einen Verlust handelt.A free block is memory that has been released, so by definition isn't leaked. Ein ignorierter Block ist ein Speicherblock, der explizit von Ihnen markiert wurde, damit er aus dem Arbeitsspeicherverlust-Bericht ausgeschlossen wird.An ignore block is memory that you've explicitly marked to exclude from the memory-leak report.

Mit den obigen Verfahren werden Speicherverluste für Arbeitsspeicher identifiziert, der mithilfe der CRT-malloc-Standardfunktion zugewiesen wurde.The preceding techniques identify memory leaks for memory allocated using the standard CRT malloc function. Belegt das Programm Speicher mithilfe des C++-Operators new, werden im Arbeitsspeicherverlust-Bericht möglicherweise nur der Dateiname und die Zeilennummer angezeigt, wo operator new _malloc_dbg aufruft.If your program allocates memory using the C++ new operator, however, you may only see the filename and line number where operator new calls _malloc_dbg in the memory-leak report. Um einen nützlicheren Arbeitsspeicherverlust-Berichts zu erstellen, können Sie ein Makro wie das folgende schreiben, das die Zeile meldet, die die Belegung vorgenommen hat:To create a more useful memory-leak report, you can write a macro like the following to report the line that made the allocation:

#ifdef _DEBUG
    #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
    // Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the
    // allocations to be of _CLIENT_BLOCK type
#else
    #define DBG_NEW new
#endif

Nun können Sie den new-Operator ersetzen, indem Sie das DBG_NEW-Makro in Ihrem Code verwenden.Now you can replace the new operator by using the DBG_NEW macro in your code. In Debugbuilds wird von DBG_NEW eine Überladung des globalen operator new verwendet, die zusätzliche Parameter für den Blocktyp, die Datei und die Zeilennummer nutzt.In debug builds, DBG_NEW uses an overload of global operator new that takes additional parameters for the block type, file, and line number. Die Überladung von new ruft _malloc_dbg auf, um die zusätzlichen Informationen zu erfassen.The overload of new calls _malloc_dbg to record the extra information. In den Arbeitsspeicherverlust-Berichten werden der Dateiname und die Zeilennummer angezeigt, in der die Blöcke mit dem Arbeitsspeicherverlust belegt wurden.The memory-leak reports show the filename and line number where the leaked objects were allocated. Releasebuilds verwenden weiterhin den Standardoperator new.Release builds still use the default new. Ein Beispiel für dieses Verfahren:Here's an example of the technique:

// debug_new.cpp
// compile by using: cl /EHsc /W4 /D_DEBUG /MDd debug_new.cpp
#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>

#ifdef _DEBUG
    #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
    // Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the
    // allocations to be of _CLIENT_BLOCK type
#else
    #define DBG_NEW new
#endif

struct Pod {
    int x;
};

void main() {
    Pod* pPod = DBG_NEW Pod;
    pPod = DBG_NEW Pod; // Oops, leaked the original pPod!
    delete pPod;

    _CrtDumpMemoryLeaks();
}

Wenn Sie diesen Code im Visual Studio-Debugger ausführen, generiert der Aufruf von _CrtDumpMemoryLeaks einen Bericht im Fenster Ausgabe, der in etwa wie folgt aussieht:When you run this code in the Visual Studio debugger, the call to _CrtDumpMemoryLeaks generates a report in the Output window that looks similar to:

Detected memory leaks!
Dumping objects ->
c:\users\username\documents\projects\debug_new\debug_new.cpp(20) : {75}
 normal block at 0x0098B8C8, 4 bytes long.
 Data: <    > CD CD CD CD
Object dump complete.

Diese Ausgabe meldet, dass die zu einem Speicherverlust führende Belegung in Zeile 20 der Datei debug_new.cpp erfolgte.This output reports that the leaked allocation was on line 20 of debug_new.cpp.

Hinweis

Es wird nicht empfohlen, ein Präprozessormakro mit dem Namen new oder einem anderen Programmiersprachen-Schlüsselwort zu erstellen.We don't recommend you create a preprocessor macro named new, or any other language keyword.

Festlegen von Haltepunkten für eine SpeicherbelegungsnummerSet breakpoints on a memory allocation number

Die Speicherbelegungsnummer gibt Aufschluss darüber, wann ein Speicherblock mit Arbeitsspeicherverlust belegt wurde.The memory allocation number tells you when a leaked memory block was allocated. Ein Block mit der Speicherbelegungsnummer 18 ist z. B. der 18. Speicherblock, der während der Ausführung der App belegt wurde.A block with a memory allocation number of 18, for example, is the 18th block of memory allocated during the run of the app. Im CRT-Bericht werden alle Speicherblockbelegungen während der Ausführung gezählt, einschließlich der Belegungen durch die CRT-Bibliothek und andere Bibliotheken wie MFC.The CRT report counts all memory-block allocations during the run, including allocations by the CRT library and other libraries such as MFC. Daher ist Speicherbelegungsblock Nummer 18 wahrscheinlich nicht der 18. Speicherblock, der von Ihrem Code zugewiesen wird.Therefore, memory allocation block number 18 probably isn't the 18th memory block allocated by your code.

Sie können die Speicherbelegungsnummer verwenden, um einen Haltepunkt für die Speicherbelegung festzulegen.You can use the allocation number to set a breakpoint on the memory allocation.

So legen Sie einen Haltepunkt für die Speicherbelegung im Überwachungsfenster fest:To set a memory-allocation breakpoint using the Watch window:

  1. Legen Sie einen Haltepunkt am Anfang der App fest, und starten Sie das Debuggen.Set a breakpoint near the start of your app, and start debugging.

  2. Wenn die App am Haltepunkt angehalten wird, öffnen Sie ein Fenster Überwachung, indem Sie Debuggen > Fenster > Überwachen 1 (bzw. Überwachen 2, Überwachen 3 oder Überwachen 4) auswählen.When the app pauses at the breakpoint, open a Watch window by selecting Debug > Windows > Watch 1 (or Watch 2, Watch 3, or Watch 4).

  3. Geben Sie im Fenster Überwachung in der Spalte Name _crtBreakAlloc ein.In the Watch window, type _crtBreakAlloc in the Name column.

    Bei Verwendung der Multithread-DLL-Version der CRT-Bibliothek (/MD-Option) fügen Sie den Kontextoperator hinzu: {,,ucrtbased.dll}_crtBreakAllocIf you're using the multithreaded DLL version of the CRT library (the /MD option), add the context operator: {,,ucrtbased.dll}_crtBreakAlloc

    Stellen Sie sicher, dass Debugsymbole geladen sind.Make sure that debug symbols are loaded. Andernfalls wird _crtBreakAlloc als nicht identifiziert gemeldet.Otherwise _crtBreakAlloc will be reported as unidentified.

  4. Drücken Sie die EINGABETASTE.Press Enter.

    Der Debugger wertet den Aufruf aus und gibt das Ergebnis in der Spalte Wert aus.The debugger evaluates the call and places the result in the Value column. Wenn Sie keine Haltepunkte für Speicherbelegungen festgelegt haben, lautet dieser Wert –1.This value will be -1 if you have not set any breakpoints on memory allocations.

  5. Ersetzen Sie den Wert in der Spalte Wert durch die Nummer der Speicherbelegung, bei der die Unterbrechung durch den Debugger erfolgen soll.In the Value column, replace the value with the allocation number of the memory allocation where you want the debugger to break.

Nachdem Sie einen Haltepunkt für eine Speicherbelegungsnummer festgelegt haben, fahren Sie mit dem Debuggen fort.After you set a breakpoint on a memory-allocation number, continue to debug. Stellen Sie sicher, dass die Ausführung unter denselben Bedingungen erfolgt, damit sich die Speicherbelegungsnummer nicht ändert.Make sure to run under the same conditions, so the memory-allocation number doesn't change. Wenn das Programm bei der angegebenen Speicherbelegung unterbrochen wird, prüfen Sie im Fenster Aufrufliste und in anderen Debuggerfenstern, unter welchen Bedingungen der Speicher belegt wurde.When your program breaks at the specified memory allocation, use the Call Stack window and other debugger windows to determine the conditions under which the memory was allocated. Anschließend können Sie die Ausführung fortsetzen, um zu beobachten, was mit dem Objekt geschieht, und festzustellen, warum es nicht ordnungsgemäß freigegeben wird.Then, you can continue execution to observe what happens to the object and determine why it isn't correctly deallocated.

Das Festlegen eines Datenhaltepunkts für das Objekt kann auch hilfreich sein.Setting a data breakpoint on the object might also be helpful. Weitere Informationen finden Sie unter Verwenden von Haltepunkten.For more information, see Using breakpoints.

Sie können auch Speicherbelegungshaltepunkte im Code festlegen.You can also set memory-allocation breakpoints in code. Sie können folgende Einstellungen vornehmen:You can set:

_crtBreakAlloc = 18;

oder:or:

_CrtSetBreakAlloc(18);

Vergleichen von SpeicherzuständenCompare memory states

Ein weiteres Verfahren zum Ermitteln von Speicherverlusten besteht darin, zu bestimmten Zeitpunkten Momentaufnahmen von Speicherzuständen der Anwendung aufzuzeichnen.Another technique for locating memory leaks involves taking snapshots of the application's memory state at key points. Um eine Momentaufnahme des Speicherzustands an einem bestimmten Punkt in der Anwendung aufzuzeichnen, erstellen Sie eine _CrtMemState-Struktur, die Sie dann an die _CrtMemCheckpoint-Funktion übergeben.To take a snapshot of the memory state at a given point in your application, create a _CrtMemState structure and pass it to the _CrtMemCheckpoint function.

_CrtMemState s1;
_CrtMemCheckpoint( &s1 );

Durch die _CrtMemCheckpoint-Funktion wird eine Momentaufnahme des aktuellen Speicherzustands in die Struktur eingefügt.The _CrtMemCheckpoint function fills in the structure with a snapshot of the current memory state.

Um den Inhalt einer _CrtMemState-Struktur auszugeben, übergeben Sie die Struktur an die _ CrtMemDumpStatistics-Funktion:To output the contents of a _CrtMemState structure, pass the structure to the _ CrtMemDumpStatistics function:

_CrtMemDumpStatistics( &s1 );

_ CrtMemDumpStatistics gibt ein Speicherabbild des Speicherzustands aus, das wie folgt aussieht:_ CrtMemDumpStatistics outputs a dump of memory state that looks like:

0 bytes in 0 Free Blocks.
0 bytes in 0 Normal Blocks.
3071 bytes in 16 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 3071 bytes.
Total allocations: 3764 bytes.

Um festzustellen, ob in einem Codeabschnitt ein Arbeitsspeicherverlust aufgetreten ist, können Sie Momentaufnahmen des Speicherzustands vor und nach dem Abschnitt erstellen und die beiden Zustände anschließend mithilfe von _ CrtMemDifference vergleichen:To determine whether a memory leak has occurred in a section of code, you can take snapshots of the memory state before and after the section, and then use _ CrtMemDifference to compare the two states:

_CrtMemCheckpoint( &s1 );
// memory allocations take place here
_CrtMemCheckpoint( &s2 );

if ( _CrtMemDifference( &s3, &s1, &s2) )
   _CrtMemDumpStatistics( &s3 );

_CrtMemDifference vergleicht die Speicherzustände s1 und s2 und gibt in (s3) ein Ergebnis zurück, das die Differenz zwischen s1 und s2 darstellt._CrtMemDifference compares the memory states s1 and s2 and returns a result in (s3) that is the difference between s1 and s2.

Eine Methode zum Suchen nach Arbeitsspeicherverlusten besteht darin, zunächst _CrtMemCheckpoint-Aufrufe am Anfang und Ende der App einzufügen und anschließend mit _CrtMemDifference die Ergebnisse zu vergleichen.One technique for finding memory leaks begins by placing _CrtMemCheckpoint calls at the beginning and end of your app, then using _CrtMemDifference to compare the results. Wenn _CrtMemDifference einen Arbeitsspeicherverlust anzeigt, können Sie weitere _CrtMemCheckpoint-Aufrufe hinzufügen, um das Programm anhand einer Binärsuche aufzuteilen, bis Sie die Quelle des Verlusts gefunden haben.If _CrtMemDifference shows a memory leak, you can add more _CrtMemCheckpoint calls to divide your program using a binary search, until you've isolated the source of the leak.

Falsch positive ErgebnisseFalse positives

_CrtDumpMemoryLeaks kann falsche Hinweise auf Arbeitsspeicherverluste geben, wenn eine Bibliothek interne Zuordnungen als normale Blöcke statt als CRT-Blöcke oder Client-Blöcke kennzeichnet._CrtDumpMemoryLeaks can give false indications of memory leaks if a library marks internal allocations as normal blocks instead of CRT blocks or client blocks. In diesem Fall kann _CrtDumpMemoryLeaks den Unterschied zwischen Benutzerspeicherbelegungen und internen Bibliotheksspeicherbelegungen nicht erkennen.In that case, _CrtDumpMemoryLeaks is unable to tell the difference between user allocations and internal library allocations. Wenn die globalen Destruktoren für die Bibliotheksspeicherbelegungen nach dem Punkt ausgeführt werden, an dem _CrtDumpMemoryLeaksaufgerufen wird, wird jede interne Bibliotheksspeicherbelegung als Arbeitsspeicherverlust angezeigt.If the global destructors for the library allocations run after the point where you call _CrtDumpMemoryLeaks, every internal library allocation is reported as a memory leak. In früheren Versionen der Standardvorlagenbibliothek (vor Visual Studio .NET) werden unter Umständen von _CrtDumpMemoryLeaks derartige falsch positive Ergebnisse ausgegeben.Versions of the Standard Template Library earlier than Visual Studio .NET may cause _CrtDumpMemoryLeaks to report such false positives.

Siehe auchSee also