Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Minnesläckor är bland de mest subtila och svåridentifieringsfelen i C/C++-appar. Minnesläckor beror på att det inte gick att frigöra minne som tidigare allokerats korrekt. En liten minnesläcka kanske inte märks först, men med tiden kan det orsaka symtom som sträcker sig från dåliga prestanda till att krascha när appen får slut på minne. En läckande app som använder allt tillgängligt minne kan orsaka att andra appar kraschar, vilket skapar förvirring om vilken app som är ansvarig. Även ofarliga minnesläckor kan tyda på andra problem som bör åtgärdas.
Visual Studio-felsökningsprogrammet och C Run-time Library (CRT) kan hjälpa dig att identifiera och identifiera minnesläckor.
Aktivera identifiering av minnesläckage
De primära verktygen för att identifiera minnesläckor är felsökningsfunktionerna C/C++ och CRT-felsöknings heap.
Om du vill aktivera alla funktioner för felsöknings-heap inkluderar du följande instruktioner i C++-programmet i följande ordning:
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
Instruktionen #define mappar en basversion av CRT-heapfunktionerna till motsvarande felsökningsversion. Om du utelämnar -instruktionen #define blir minnesläckagedumpen mindre detaljerad.
Inklusive crtdbg.h mappar malloc funktionerna och free till deras felsökningsversioner och _malloc_dbg_free_dbg, som spårar minnesallokering och frigöring. Den här mappningen sker endast i felsökningsversioner som har _DEBUG. Versionsversioner använder vanliga malloc funktioner och free funktioner.
När du har aktiverat funktionerna för felsökning av heap med hjälp av föregående instruktioner, gör du ett anrop till _CrtDumpMemoryLeaks före en appavslutpunkt för att visa en rapport om minnesläckage när appen avslutas.
_CrtDumpMemoryLeaks();
Om appen har flera avslut behöver du inte placera _CrtDumpMemoryLeaks den manuellt vid varje slutpunkt. Om du vill orsaka ett automatiskt anrop till _CrtDumpMemoryLeaks vid varje slutpunkt, gör du ett anrop till _CrtSetDbgFlag i början av appen med bitfälten som visas här:
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
Som standard _CrtDumpMemoryLeaks matar ut rapporten med minnesläckage till felsökningsfönstret i utdatafönstret . Om du använder ett bibliotek kan biblioteket återställa utdata till en annan plats.
Du kan använda _CrtSetReportMode för att omdirigera rapporten till en annan plats eller tillbaka till utdatafönstret som du ser här:
_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_DEBUG );
I följande exempel visas en enkel minnesläcka och information om minnesläcka med hjälp av _CrtDumpMemoryLeaks();.
// debug_malloc.cpp
// compile by using: cl /EHsc /W4 /D_DEBUG /MDd debug_malloc.cpp
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <iostream>
int main()
{
std::cout << "Hello World!\n";
int* x = (int*)malloc(sizeof(int));
*x = 7;
printf("%d\n", *x);
x = (int*)calloc(3, sizeof(int));
x[0] = 7;
x[1] = 77;
x[2] = 777;
printf("%d %d %d\n", x[0], x[1], x[2]);
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
_CrtDumpMemoryLeaks();
}
Tolka rapporten med minnesläckage
Om appen inte definierar _CRTDBG_MAP_ALLOCvisar _CrtDumpMemoryLeaks en rapport om minnesläckage som ser ut så här:
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.
Om appen definierar _CRTDBG_MAP_ALLOCser rapporten för minnesläckage ut så här:
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.
Den andra rapporten visar filnamnet och radnumret där det läckta minnet först allokeras.
Oavsett om du definierar _CRTDBG_MAP_ALLOCvisas rapporten för minnesläckage:
- Minnesallokeringsnumret, som finns
18i exemplet - Blocktypen i
normalexemplet. - Platsen för hexadecimalt minne
0x00780E80i exemplet. - Storleken på blocket i
64 bytesexemplet. - De första 16 byteen av data i blocket, i hexadecimal form.
Minnesblockstyper är normala, klient eller CRT. Ett normalt block är vanligt minne som allokeras av programmet. Ett klientblock är en särskild typ av minnesblock som används av MFC-program för objekt som kräver en destruktor. MFC-operatorn new skapar antingen ett normalt block eller ett klientblock, beroende på vad som är lämpligt för objektet som skapas.
Ett CRT-block allokeras av CRT-biblioteket för eget bruk. CRT-biblioteket hanterar frilokaliseringen för dessa block, så CRT-block visas inte i rapporten med minnesläckage om det inte finns allvarliga problem med CRT-biblioteket.
Det finns två andra typer av minnesblock som aldrig visas i rapporter om minnesläckage. Ett ledigt block är minne som har släppts, så per definition inte läckt. Ett ignorerande block är minne som du uttryckligen har markerat för att undanta från rapporten med minnesläckage.
De föregående teknikerna identifierar minnesläckor för minne som allokerats med hjälp av crt-standardfunktionen malloc . Om programmet allokerar minne med C++ new -operatorn kanske du dock bara ser filnamnet och radnumret där operator new anrop _malloc_dbg i rapporten med minnesläckage. Om du vill skapa en mer användbar rapport över minnesläckage kan du skriva ett makro som följande för att rapportera raden som gjorde allokeringen:
#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
Nu kan du ersätta operatorn new med hjälp av makrot DBG_NEW i koden. I felsökningsversioner DBG_NEW använder en överbelastning av global operator new som tar extra parametrar för blocktyp, fil och radnummer. Överbelastningen av new anrop _malloc_dbg för att registrera extra information. Rapporterna om minnesläckage visar filnamnet och radnumret där de läckta objekten allokerades. Versionsversioner använder fortfarande standardvärdet new. Här är ett exempel på tekniken:
// 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;
};
int main() {
Pod* pPod = DBG_NEW Pod;
pPod = DBG_NEW Pod; // Oops, leaked the original pPod!
delete pPod;
_CrtDumpMemoryLeaks();
}
När du kör den här koden i Visual Studio-felsökningsprogrammet genererar anropet till _CrtDumpMemoryLeaks en rapport i utdatafönstret som ser ut ungefär så här:
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.
Det här utdata rapporterar att den läckta allokeringen fanns på rad 20 i debug_new.cpp.
Anmärkning
Vi rekommenderar inte att du skapar ett förprocessormakro med namnet new, eller något annat språknyckelord.
Ange brytpunkter för ett minnesallokeringsnummer
Minnesallokeringsnumret anger när ett läckt minnesblock allokerades. Ett block med ett minnesallokeringsnummer på 18 är till exempel det 18:e minnesblocket som allokeras under körningen av appen. CRT-rapporten räknar alla minnesblocksallokeringar under körningen, inklusive allokeringar av CRT-biblioteket och andra bibliotek, till exempel MFC. Därför är minnesallokeringsblock nummer 18 förmodligen inte det 18:e minnesblocket som allokeras av koden.
Du kan använda allokeringsnumret för att ange en brytpunkt för minnesallokeringen.
Så här anger du en brytpunkt för minnesallokering med hjälp av bevakningsfönstret
Ange en brytpunkt nära början av din app och börja felsöka.
När appen pausar vid brytpunkten öppnar du ett bevakningsfönster genom att välja Felsöka>Windows>Watch 1 (eller Watch 2, Watch 3 eller Watch 4).
I fönstret Klocka skriver du
_crtBreakAlloci kolumnen Namn .Om du använder den flertrådade DLL-versionen av CRT-biblioteket (alternativet /MD) lägger du till kontextoperatorn:
{,,ucrtbased.dll}_crtBreakAllocKontrollera att felsökningssymbolerna läses in. Annars
_crtBreakAllocrapporteras som oidentifierad.Tryck på Retur.
Felsökningsprogrammet utvärderar anropet och placerar resultatet i kolumnen Värde . Det här värdet är -1 om du inte har angett några brytpunkter för minnesallokeringar.
I kolumnen Värde ersätter du värdet med allokeringsnumret för den minnesallokering där du vill att felsökaren ska brytas.
När du har angett en brytpunkt för ett minnesallokeringsnummer fortsätter du att felsöka. Se till att köras under samma förhållanden, så att minnesallokeringsnumret inte ändras. När programmet bryts vid den angivna minnesallokeringen använder du fönstret Anropa stack och andra felsökningsfönster för att fastställa under vilka förhållanden minnet allokerades. Sedan kan du fortsätta körningen för att se vad som händer med objektet och avgöra varför det inte frigörs korrekt.
Det kan också vara användbart att ange en data brytpunkt för objektet. Mer information finns i Använda brytpunkter.
Du kan också ange brytpunkter för minnesallokering i kod. Du kan ange:
_crtBreakAlloc = 18;
eller:
_CrtSetBreakAlloc(18);
Jämför minnestillstånd
En annan teknik för att hitta minnesläckor är att ta ögonblicksbilder av programmets minnestillstånd vid viktiga punkter. Om du vill ta en ögonblicksbild av minnestillståndet vid en viss tidpunkt i programmet skapar du en _CrtMemState struktur och skickar den till _CrtMemCheckpoint funktionen.
_CrtMemState s1;
_CrtMemCheckpoint( &s1 );
Funktionen _CrtMemCheckpoint fyller i strukturen med en ögonblicksbild av det aktuella minnestillståndet.
Skicka strukturen till funktionen för att mata ut innehållet i _CrtMemState en _CrtMemDumpStatistics struktur:
_CrtMemDumpStatistics( &s1 );
_CrtMemDumpStatistics matar ut en dump med minnestillstånd som ser ut så här:
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.
För att avgöra om en minnesläcka har inträffat i ett kodavsnitt kan du ta ögonblicksbilder av minnestillståndet före och efter avsnittet och sedan använda _CrtMemDifference för att jämföra de två tillstånden:
_CrtMemCheckpoint( &s1 );
// memory allocations take place here
_CrtMemCheckpoint( &s2 );
if ( _CrtMemDifference( &s3, &s1, &s2) )
_CrtMemDumpStatistics( &s3 );
_CrtMemDifference jämför minnestillstånden s1 och s2 och returnerar ett resultat i (s3) som är skillnaden mellan s1 och s2.
En teknik för att hitta minnesläckor börjar med att ringa _CrtMemCheckpoint anrop i början och slutet av appen och sedan använda _CrtMemDifference för att jämföra resultaten. Om _CrtMemDifference visar en minnesläcka kan du lägga till fler _CrtMemCheckpoint anrop för att dela upp programmet med hjälp av en binär sökning tills du har isolerat källan till läckan.
Falska positiva
_CrtDumpMemoryLeaks kan ge falska indikationer på minnesläckor om ett bibliotek markerar interna allokeringar som normala block i stället för CRT-block eller klientblock. I så fall _CrtDumpMemoryLeaks kan inte skilja mellan användarallokeringar och interna biblioteksallokeringar. Om de globala destruatörerna för biblioteksallokeringarna körs efter den punkt där du anropar _CrtDumpMemoryLeaksrapporteras varje intern biblioteksallokering som en minnesläcka. Versioner av standardmallbiblioteket tidigare än Visual Studio .NET kan orsaka _CrtDumpMemoryLeaks att sådana falska positiva identifieringar rapporteras.