Detecting and Isolating Memory Leaks Using Microsoft Visual C++

Edward Wright
Microsoft Corporation

May 1999

Summary: Describes how to use the tools provided by the Microsoft® Visual C++® debugger and CRT library to locate and isolate memory leaks, hard-to-detect bugs that can cause problematic behavior in applications developed with the C/C++ programming language. (7 printed pages) Covers:

Introduction
Enabling Memory Leak Detection
     Using _CrtSetDbgFlag
     Interpreting Memory Block Types
Setting the CRT Report Mode
Setting a Breakpoint on a Memory Allocation Number
Comparing Memory States
Additional Information

Introduction

The ability to dynamically allocate and deallocate memory is one of the strongest features of the C/C++ programming language, but as the Chinese philosopher Sun Tzu pointed out, the greatest strength can also be the greatest weakness. This is certainly true of C/C++ applications, where memory-handling mistakes are among the most common sources of bugs. One of the most subtle and hard-to-detect bugs is the memory leak—the failure to properly deallocate memory that was previously allocated. A small memory leak that occurs only once may not be noticed, but programs that leak large amounts of memory, or leak progressively, may display symptoms ranging from poor (and gradually decreasing) performance to complete failure as a result of running out of memory. Worse, a leaking program may use up so much memory that it causes another program to fail, leaving the user with no clue as to where the problem truly lies. In addition, even a harmless memory leak may be symptomatic of other problems.

Fortunately, the Visual C++ debugger and CRT library provide you with an effective set of tools for detecting and identifying memory leaks. This article describes how to use these tools to effectively and systematically isolate memory leaks.

Enabling Memory Leak Detection

The primary tools for detecting memory leaks are the debugger and the CRT debug heap functions. To enable the debug heap functions, you must include the following statements in your program:

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

The #include statements must be in the order just shown. If you change the order, the functions you will use may not work properly. Including crtdbg.h maps the malloc and free functions to their debug versions, _malloc_dbg and _free_dbg, which keep track of memory allocation and deallocation. This mapping occurs only in a debug build (that is, only when _DEBUG is defined). Release builds use the ordinary malloc and free functions.

The #define statement maps the base versions of the CRT heap functions to the corresponding debug versions. This statement is not required, but without it, the memory leak dump will contain less useful information.

Once you have added the statements just shown, you can dump memory leak information by including the following statement in your program:

_CrtDumpMemoryLeaks();

When you run your program under the debugger, _CrtDumpMemoryLeaks displays memory leak information in the Debug tab of the Output window. The memory leak information looks like this:

Detected memory leaks!
Dumping objects ->
C:\PROGRAM FILES\VISUAL STUDIO\MyProjects\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.

If you did not use the #define _CRTDBG_MAP_ALLOC statement, the memory leak dump would look 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.

As you can see, _CrtDumpMemoryLeaks gives you much more useful information when _CRTDBG_MAP_ALLOC is defined. Without _CRTDBG_MAP_ALLOC defined, the display shows you:

With _CRTDBG_MAP_ALLOC defined, the display also shows you the file where the leaked memory was allocated. The number in parentheses following the file name (20, in this example) is the line number within the file. If you double-click the output line containing the line number and file name

C:\PROGRAM FILES\VISUAL STUDIO\MyProjects\leaktest\leaktest.cpp(20) : {18} normal block at 0x00780E80, 64 bytes long.

the cursor will jump to the line where the memory is allocated in the source file (in this case, line 20 of leaktest.cpp). Selecting the output line and pressing F4 will have the same effect.

Using _CrtSetDbgFlag

Calling _CrtDumpMemoryLeaks is easy enough if your program always exits in the same place. But, what if your program can exit from multiple locations? Instead of putting a call to _CrtDumpMemoryLeaks at each possible exit, you can include the following call at the beginning of your program:

_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

This statement automatically calls _CrtDumpMemoryLeaks when your program exits. You must set both bit fields, _CRTDBG_ALLOC_MEM_DF and _CRTDBG_LEAK_CHECK_DF, as shown previously.

Interpreting Memory Block Types

As stated earlier, the memory leak information identifies each block of leaked memory as a normal block, a client block, or a CRT block. In practice, normal blocks and client blocks are the only types you are likely to see.

There are two block types you will never see in the memory leak information:

Setting the CRT Report Mode

By default, _CrtDumpMemoryLeaks dumps memory leak information to the Debug pane of the Output window, as described earlier. You can reset this to dump to another location using _CrtSetReportMode. If you use a library, it may reset the output to another location. In that case, you can set the output location back to the Output window using the following statement:

_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );

For information on using _CrtSetReportMode to send output to other locations, see the _CrtSetReportMode section of the Visual C++ documentation (Visual C++ Programmer’s Guide, Run-Time Library Reference, Debug Function Reference).

Setting a Breakpoint on a Memory Allocation Number

The file name and line number in the memory leak report tell you where leaked memory is allocated, but knowing where the memory is allocated is not always sufficient to identify the problem. Often an allocation will be called many times during a run of the program, but it may leak memory only on certain calls. To identify the problem, you must know not only where the leaked memory is allocated but also the conditions under which the leak occurs. The piece of information that makes it possible for you to do this is the memory allocation number. This is the number that appears in curly braces, after the file name and line number when those are displayed. For example, in the following output, “18” is the memory allocation number. It means the leaked memory is the 18th block of memory allocated in your program.

Detected memory leaks!
Dumping objects ->
C:\PROGRAM FILES\VISUAL STUDIO\MyProjects\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.

The CRT library counts all memory blocks allocated during a run of the program, including memory allocated by the CRT library itself or by other libraries, such as MFC. Therefore, an object with allocation number n will be the nth object allocated in your program but may not be the nth object allocated by your code. (In most cases, it will not be.)

You can use the allocation number to set a breakpoint at the location where memory is allocated. To do this, set a location breakpoint near the start of your program. When your program breaks at that point, you can set such a memory allocation breakpoint from the QuickWatch dialog box or the Watch window. In the Watch window, for example, type the following expression in the Name column:

_crtBreakAlloc

If you are using the multithreaded dynamic-link library (DLL) version of the CRT library (the /MD option), you must include the context operator, as shown here:

{,,msvcrtd.dll}_crtBreakAlloc

Now, press RETURN. The debugger evaluates the call and places the result in the Value column. This value will be –1 if you have not set any breakpoints on memory allocations. Replace the value in the Value column with the allocation number of the memory allocation where you want to break—for example, 18 to break at the allocation shown in the output earlier.

After you set breakpoints on the memory allocations in which you are interested, you can continue debugging. Be careful to run the program under the same conditions as the previous run so the allocation order does not change. When your program breaks at the specified memory allocation, you can look at the Call Stack window and other debugger information to determine the conditions under which the memory was allocated. If necessary, you can continue execution of the program from that point to see what happens to the object and perhaps determine why it is not properly deallocated. (Setting a Data breakpoint on the object may be helpful.)

Although it is usually easier to set memory allocation breakpoints in the debugger, you can set them in your code, if you prefer. To set a memory allocation breakpoint in your code, add a line like this (for the 18th memory allocation):

_crtBreakAlloc = 18;

As an alternative, you can use the _CrtSetBreakAlloc function, which has the same effect:

_CrtSetBreakAlloc(18);

Comparing Memory States

Another technique for locating memory leaks involves taking snapshots of the application’s memory state at key points. The CRT library provides a structure type, _CrtMemState, which you can use to store a snapshot of the memory state:

_CrtMemState s1, s2, s3;

To take a snapshot of the memory state at a given point, pass a _CrtMemState structure to the _CrtMemCheckpoint function. This function fills in the structure with a snapshot of the current memory state:

_CrtMemCheckpoint( &s1 );

You can dump the contents of a _CrtMemState structure at any point by passing the structure to the _CrtMemDumpStatistics function:

_CrtMemDumpStatistics( &s3 );( &s1 );

This function prints a dump of memory allocation information 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.

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 );

As the name implies, _CrtMemDifference compares two memory states (the first two parameters) and produces a result (the third parameter) that is the difference of the two states. Placing _CrtMemCheckpoint calls at the beginning and end of your program and using _CrtMemDifference to compare the results provides another way to check for memory leaks. If a leak is detected, you can use _CrtMemCheckpoint calls to divide your program and locate the leak using binary search technique.

Additional Information

The following article discusses memory leak debugging, with special emphasis on MFC applications:

The following book contains no information on debugging memory leaks, but may prove useful if you are writing a white paper and want a quote from an ancient Chinese philosopher: