Recherche de fuites de mémoire à l'aide de la bibliothèque CRTFinding Memory Leaks Using the CRT Library

Les fuites de mémoire, qui correspondent à l'échec de désallocation de mémoire précédemment allouée, figurent parmi les bogues les plus difficiles à détecter des applications C/C++.Memory leaks, defined as the failure to correctly deallocate memory that was previously allocated, are among the most subtle and hard-to-detect bugs in C/C++ applications. Au début, vous pourrez remarquer une petite fuite de mémoire, mais au fil du temps, cette fuite de mémoire peut progressivement provoquer des problèmes allant d'une perte de performances au blocage de l'application, lorsque la mémoire devient insuffisante.A small memory leak might not be noticed at first, but over time, a progressive memory leak can cause symptoms that range from decreased performance to crashing when the application runs out of memory. Pire encore, une application connaissant une fuite de mémoire qui utiliserait toute la mémoire disponible pourrait entraîner le blocage d'une autre application, provoquant le doute quant à l'application réellement responsable du blocage.Worse, a leaking application that uses up all available memory can cause another application to crash, creating confusion as to which application is responsible. Même les fuites de mémoire apparemment sans incidence peuvent être responsables d'autres problèmes devant être corrigés.Even seemingly harmless memory leaks might be symptomatic of other problems that should be corrected.

Le débogueur Visual StudioVisual Studio et les bibliothèques runtime C (CRT) vous offrent la possibilité de détecter et d'identifier les fuites de mémoire.The Visual StudioVisual Studio debugger and C Run-Time (CRT) libraries provide you with the means for detecting and identifying memory leaks.

Activation de la détection des fuites de mémoireEnabling Memory Leak Detection

Les principaux outils de détection des fuites de mémoire sont le débogueur et les fonctions du tas de débogage des bibliothèques Runtime C (CRT).The primary tools for detecting memory leaks are the debugger and the C Run-Time Libraries (CRT) debug heap functions.

Pour activer les fonctions du tas de débogage, incluez les instructions suivantes dans votre programme :To enable the debug heap functions, include the following statements in your program:

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

Pour que les CRT fonctionnent correctement, les instructions #include doivent respecter l'ordre indiqué ici.For the CRT functions to work correctly, the #include statements must follow the order shown here.

L'inclusion de crtdbg.h a pour résultat le mappage des fonctions malloc et free vers leurs versions Debug, _malloc_dbg et free, qui effectuent le suivi de l'allocation et de la désallocation de mémoire.Including crtdbg.h maps the malloc and the free functions to their debug versions, _malloc_dbg and free, which track memory allocation and deallocation. Ce mappage se produit uniquement dans les versions Debug avec _DEBUG.This mapping occurs only in debug builds, which have _DEBUG. Les versions Release utilisent les fonctions malloc et free ordinaires.Release builds use the ordinary malloc and free functions.

L'instruction #define mappe une version de base des fonctions de tas CRT vers la version Debug correspondante.The #define statement maps a base version of the CRT heap functions to the corresponding debug version. Si vous omettez l'instruction #define , le dump de fuite de mémoire sera moins détaillé.If you omit the #define statement, the memory leak dump will be less detailed.

Après avoir activé les fonctions de tas de débogage à l'aide de ces instructions, vous pouvez appeler _CrtDumpMemoryLeaks avant un point de sortie de l'application, de façon à afficher un rapport des fuites de mémoire lorsque vous quittez l'application :After you have enabled the debug heap functions by using these statements, you can place a call to _CrtDumpMemoryLeaks before an application exit point to display a memory-leak report when your application exits:

_CrtDumpMemoryLeaks();  

Si votre application dispose de plusieurs sorties, il n'est pas nécessaire d'appeler manuellement _CrtDumpMemoryLeaks à chaque point de sortie.If your application has multiple exits, you do not need to manually place a call to _CrtDumpMemoryLeaks at every exit point. Si vous appelez _CrtSetDbgFlag au début de votre application, _CrtDumpMemoryLeaks sera automatiquement appelé à chaque point de sortie.A call to _CrtSetDbgFlag at the beginning of your application will cause an automatic call to _CrtDumpMemoryLeaks at each exit point. Vous devez définir les deux champs de bits indiqués ci-dessous :You must set the two bit fields shown here:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );  

Par défaut, _CrtDumpMemoryLeaks affiche un rapport des fuites de mémoire dans le volet Débogage de la fenêtre Sortie .By default, _CrtDumpMemoryLeaks outputs the memory-leak report to the Debug pane of the Output window. Vous pouvez utiliser _CrtSetReportMode pour rediriger le rapport vers un autre emplacement.You can use _CrtSetReportMode to redirect the report to another location.

Si vous utilisez une bibliothèque, celle-ci peut redéfinir la sortie vers un autre emplacement.If you use a library, the library might reset the output to another location. Dans ce cas, vous pouvez redéfinir l'emplacement de sortie vers la fenêtre Sortie comme indiqué ci-dessous :In that case, you can set the output location back to the Output window, as shown here:

_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );  

Interprétation du rapport des fuites de mémoireInterpreting the Memory-Leak Report

Si votre application ne définit pas _CRTDBG_MAP_ALLOC, _CrtDumpMemoryLeaks affiche un rapport des fuites de mémoire similaire à celui-ci :If your application does not define _CRTDBG_MAP_ALLOC, _CrtDumpMemoryLeaks displays a memory-leak report that looks like this:

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.  

Si votre application définit _CRTDBG_MAP_ALLOC, le rapport des fuites de mémoire ressemblera à ceci :If your application defines _CRTDBG_MAP_ALLOC, the memory-leak report looks like this:

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.  

À la différence du premier, le deuxième rapport indique le nom du fichier et le numéro de ligne où la mémoire perdue est d'abord allouée.The difference is that the second report shows name of the file and the line number where the leaked memory is first allocated.

Que vous définissiez _CRTDBG_MAP_ALLOC ou non, le rapport des fuites de mémoire affiche les informations suivantes :Whether you define _CRTDBG_MAP_ALLOC or not, the memory-leak report will display the following information:

  • le numéro d'allocation de mémoire, en l'occurrence 18 ;The memory allocation number, which is 18 in this example

  • le type de bloc, en l'occurrence normal ;The block type, which is normal in this example.

  • l'emplacement de stockage hexadécimal, en l'occurrence 0x00780E80 ;The hexadecimal memory location, which is 0x00780E80 in this example.

  • la taille du bloc, en l'occurrence 64 bytes ;The size of the block, 64 bytes in this example.

  • les 16 premiers octets de données du bloc, au format hexadécimal.The first 16 bytes of data in the block, in hexadecimal form.

    Le rapport des fuites de mémoire identifie un bloc de mémoire comme étant normal, client ou CRT.The memory-leak report identifies a block of memory as normal, client, or CRT. Un bloc normal est un bloc de mémoire ordinaire alloué par votre programme.A normal block is ordinary memory allocated by your program. Un bloc client est un type de bloc de mémoire spécial utilisé par les programmes MFC pour les objets qui nécessitent un destructeur.A client block is a special type of memory block used by MFC programs for objects that require a destructor. Suivant l'objet créé, l'opérateur MFC new crée soit un bloc normal, soit un bloc client.The MFC new operator creates either a normal block or a client block, as appropriate for the object being created. Un bloc CRT est alloué par la bibliothèque CRT pour ses propres besoins.A CRT block is allocated by the CRT library for its own use. La bibliothèque CRT gère la désallocation de ces blocs.The CRT library handles the deallocation for these blocks. Par conséquent, il est peu probable qu'ils apparaissent dans le rapport des fuites de mémoire, sauf en cas de problème important, comme par exemple, si la bibliothèque CRT est endommagée.Therefore, it is unlikely you will see these in the memory leak report unless something is significantly wrong, for example, the CRT library is corrupted.

    Il existe deux autres types de blocs de mémoire qui n'apparaissent jamais dans les rapports de fuite de mémoire.There are two other types of memory blocks that never appear in memory-leak reports. D'abord, le bloc libre , qui correspond à un bloc de mémoire libéré.A free block is memory that has been released. Il n'a donc pas fait l'objet d'une fuite, par définition.That means it is not leaked, by definition. Ensuite, le bloc ignore , qui correspond à de la mémoire explicitement marquée comme étant à exclure du rapport des fuites de mémoire.An ignore block is memory that you have explicitly marked to exclude it from the memory-leak report.

    Ces techniques fonctionnent pour la mémoire allouée à l'aide de la fonction CRT standard malloc .These techniques work for memory allocated using the standard CRT malloc function. Si votre programme alloue de la mémoire à l’aide de C++ new (opérateur), cependant, vous pouvez uniquement voir le fichier et numéro de ligne où l’implémentation de global operator new appelle _malloc_dbg dans le rapport des fuites de mémoire.If your program allocates memory using the C++ new operator, however, you may only see the file and line number where the implementation of global operator new calls _malloc_dbg in the memory-leak report. Étant donné que ce comportement n’est pas très utile, vous pouvez le modifier pour signaler la ligne qui a effectué l’allocation à l’aide d’une macro qui ressemble à ceci :Because that behavior is not very useful, you can change it to report the line that made the allocation by using a macro that looks like this:

#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

Maintenant que vous pouvez remplacer le new opérateur à l’aide de la DBG_NEW macro dans votre code.Now you can replace the new operator by using the DBG_NEW macro in your code. Dans les versions debug, il utilise une surcharge de global operator new qui accepte des paramètres supplémentaires pour le type de bloc, le fichier et le numéro de ligne.In debug builds, this uses an overload of global operator new that takes additional parameters for the block type, file, and line number. Cette surcharge de new appelle _malloc_dbg pour enregistrer les informations supplémentaires.This overload of new calls _malloc_dbg to record the extra information. Lorsque vous utilisez DBG_NEW, afficher le nom de fichier et numéro de ligne où les objets ayant fuit alloués des rapports de la fuite de mémoire.When you use DBG_NEW, the memory leak reports show the filename and line number where the leaked objects were allocated. Dans les versions commerciales, il utilise la valeur par défaut new.In retail builds, it uses the default new. (Nous ne recommandons pas créer de préprocesseur ou macro nommée new, ou tout autre mot clé language.) Voici un exemple de la technique :(We do not recommend you create a preprocessor macro named new, or any other language keyword.) 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();
}

Lorsque vous exécutez ce code dans le débogueur dans Visual Studio, l’appel à _CrtDumpMemoryLeaks génère un rapport dans le sortie fenêtre ressemble à ceci :When you run this code in the debugger in Visual Studio, the call to _CrtDumpMemoryLeaks generates a report in the Output window that looks similar to this:

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.

Vous pouvez en déduire que l’allocation de fuite a à la ligne 20 de debug_new.cpp.This tells you that the leaked allocation was on line 20 of debug_new.cpp.

Définition de points d'arrêt sur un numéro d'allocation de mémoireSetting Breakpoints on a Memory Allocation Number

Le numéro d'allocation de mémoire vous indique lorsqu'un bloc de mémoire perdue a été alloué.The memory allocation number tells you when a leaked memory block was allocated. Un bloc dont le numéro d'allocation de mémoire est égal à 18 correspond au 18e bloc de mémoire alloué pendant l'exécution de l'application.A block with a memory allocation number of 18, for example, is the 18th block of memory allocated during the run of the application. Le rapport CRT compte toutes les allocations de blocs de mémoire pendant l'exécution.The CRT report counts all memory-block allocations during the run. Cela comprend les allocations effectuées par la bibliothèque CRT et d'autres bibliothèques telles que MFC.This includes allocations by the CRT library and other libraries such as MFC. Par conséquent, un bloc dont le numéro d'allocation de mémoire est égal à 18 peut ne pas être le 18e bloc de mémoire alloué par votre code.Therefore, a block with a memory allocation number of 18 may not be the 18th memory block allocated by your code. En général, ce ne sera pas le cas.Typically, it will not be.

Vous pouvez utiliser le numéro d'allocation pour définir un point d'arrêt sur l'allocation de mémoire.You can use the allocation number to set a breakpoint on the memory allocation.

Pour définir un point d'arrêt d'allocation de mémoire à l'aide de la fenêtre EspionTo set a memory-allocation breakpoint using the Watch window

  1. Définissez un point d'arrêt vers le début de votre application, puis démarrez-la.Set a breakpoint near the start of your application, and then start your application.

  2. Lorsque l'application s'arrête au point d'arrêt, ouvrez la fenêtre Espion .When the application breaks at the breakpoint, the Watch window.

  3. Depuis la fenêtre Espion , tapez _crtBreakAlloc dans la colonne Nom .In the Watch window, type _crtBreakAlloc in in the Name column.

    Si vous utilisez la version DLL multithread de la bibliothèque CRT (l'option /MD), incluez l'opérateur de contexte : {,,ucrtbased.dll}_crtBreakAllocIf you are using the multithreaded DLL version of the CRT library (the /MD option), include the context operator: {,,ucrtbased.dll}_crtBreakAlloc

  4. Appuyez sur RETOUR.Press RETURN.

    Le débogueur évalue l'expression et place le résultat dans la colonne Valeur .The debugger evaluates the call and places the result in the Value column. Cette valeur sera -1 si vous n’avez pas défini les points d’arrêt sur les allocations de mémoire.This value will be -1 if you have not set any breakpoints on memory allocations.

  5. Remplacez la valeur dans la colonne Valeur par le numéro de l'allocation de mémoire où vous souhaitez effectuer l'arrêt.In the Value column, replace the value shown with the allocation number of the memory allocation where you want to break.

    Une fois le point d'arrêt défini sur un numéro d'allocation de mémoire, vous pouvez poursuivre le débogage.After you set a breakpoint on a memory-allocation number, you can continue to debug. Veillez à exécuter le programme dans les mêmes conditions que lors de la précédente exécution, de manière à conserver le même ordre d'allocation de mémoire.Be careful to run the program under the same conditions as the previous run so that the memory-allocation order does not change. Lorsque votre programme s'arrête à l'allocation de mémoire spécifiée, vous pouvez utiliser la fenêtre Pile des appels et les autres fenêtres de débogage pour déterminer dans quelles conditions la mémoire a été allouée.When your program breaks at the specified memory allocation, you can use the Call Stack window and other debugger windows to determine the conditions under which the memory was allocated. Ensuite, vous pouvez poursuivre l'exécution pour observer ce qui arrive à l'objet et connaître la raison pour laquelle il n'a pas été désalloué.Then, you can continue execution to observe what happens to the object and determine why it is not correctly deallocated.

    La définition d’un point d’arrêt sur variable peut aussi s’avérer utile.Setting a data breakpoint on the object might also be helpful. Pour plus d’informations, consultez Using Breakpoints.For more information, see Using Breakpoints.

    Vous pouvez aussi définir des points d’arrêt d’allocation de mémoire dans le code.You can also set memory-allocation breakpoints in code. Il existe deux façons d'effectuer cette opération :There are two ways to do this:

_crtBreakAlloc = 18;  

ou :or:

_CrtSetBreakAlloc(18);  

Comparaison des états de la mémoireComparing Memory States

Une autre technique pour détecter les fuites de mémoire consiste à prendre des instantanés de l'état de la mémoire de l'application en certains points clés.Another technique for locating memory leaks involves taking snapshots of the application's memory state at key points. Pour prendre un instantané de l'état de la mémoire à un point donné de votre application, créez une structure _CrtMemState et passez-la à la fonction _CrtMemCheckpoint .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. Cette fonction remplit la structure avec un instantané de l'état actuel de la mémoire :This function fills in the structure with a snapshot of the current memory state:

_CrtMemState s1;  
_CrtMemCheckpoint( &s1 );  

_CrtMemCheckpoint remplit la structure avec un instantané de l'état actuel de la mémoire :_CrtMemCheckpoint fills in the structure with a snapshot of the current memory state.

Pour afficher le contenu d'une structure _CrtMemState , passez-la à la fonction _ CrtMemDumpStatistics :To output the contents of a _CrtMemState structure, pass the structure to the _ CrtMemDumpStatistics function:

_CrtMemDumpStatistics( &s1 );  

_ CrtMemDumpStatistics crée un dump de l'état de la mémoire similaire à celui-ci :_ CrtMemDumpStatistics outputs a dump of memory state that looks like this:

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.  

Pour déterminer si une fuite de mémoire a eu lieu dans une section de code, vous pouvez prendre des instantanés de l'état de la mémoire avant et après la section, puis utiliser _ CrtMemDifference pour comparer les deux états :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 compare les états de mémoire s1 et s2 , puis retourne un résultat (s3) qui correspond à la différence entre s1 et s2._CrtMemDifference compares the memory states s1 and s2 and returns a result in (s3) that is the difference of s1 and s2.

L'une des techniques permettant de rechercher les fuites de mémoire consiste à appeler _CrtMemCheckpoint au début et à la fin de votre application, puis à utiliser _CrtMemDifference pour comparer les résultats.One technique for finding memory leaks begins by placing _CrtMemCheckpoint calls at the beginning and end of your application, then using _CrtMemDifference to compare the results. Si _CrtMemDifference indique une fuite de mémoire, vous pouvez ajouter davantage d'appels à _CrtMemCheckpoint pour diviser votre programme à l'aide d'une recherche binaire jusqu'à ce que vous ayez isolé la source des fuites.If _CrtMemDifference shows a memory leak, you can add more _CrtMemCheckpoint calls to divide your program using a binary search until you have isolated the source of the leak.

Faux positifsFalse positives

Dans certains cas, _CrtDumpMemoryLeaks peut donner de fausses indications de fuites de mémoire.In some cases, _CrtDumpMemoryLeaks can give false indications of memory leaks. Cela peut se produire si vous utilisez une bibliothèque qui marque des allocations internes comme _NORMAL_BLOCK au lieu de _CRT_BLOCKou _CLIENT_BLOCK.This might occur if you use a library that marks internal allocations as _NORMAL_BLOCKs instead of _CRT_BLOCKs or _CLIENT_BLOCKs. Dans ce cas, _CrtDumpMemoryLeaks est incapable d'indiquer la différence entre les allocations d'utilisateur et les allocations de bibliothèque internes.In that case, _CrtDumpMemoryLeaks is unable to tell the difference between user allocations and internal library allocations. Si les destructeurs globaux des allocations de la bibliothèque s'exécutent après le point d'appel à _CrtDumpMemoryLeaks, chaque allocation de bibliothèque interne est signalée comme une fuite de mémoire.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. Dans les versions plus anciennes de la bibliothèque de modèles standard antérieures à Visual Studio .NET, _CrtDumpMemoryLeaks rapportait de tels faux positifs, mais ce problème a été résolu dans les versions récentes.Older versions of the Standard Template Library, earlier than Visual Studio .NET, caused _CrtDumpMemoryLeaks to report such false positives, but this has been fixed in recent releases.

Voir aussiSee Also

Détails du tas de débogage CRT CRT Debug Heap Details
Sécurité du débogueur Debugger Security
Débogage du code natifDebugging Native Code