Learn how heap management is done in a debug build
Rating:





| Marius Bancila (view profile) April 4, 2005 |
Environment: Visual C++ 6.0
When you compile a debug build of your program with Visual Studio and run it in debugger, you can see that the memory allocated or deallocated has funny values, such as 0xCDCDCDCD or 0xDDDDDDDD. This is the result of the work Microsoft has put in to detect memory corruption and leaks in the Win32 platform. In this article, I will explain how memory allocation/deallocation is done via new/delete or malloc/free.
First, I will explain what all these values that you see, like CD, DD, and so forth, mean.
Value | Name | Description |
---|---|---|
0xCD | Clean Memory | Allocated memory via malloc or new but never written by the application. |
0xDD | Dead Memory | Memory that has been released with delete or free. It is used to detect writing through dangling pointers. |
0xFD | Fence Memory | Also known as "no mans land." This is used to wrap the allocated memory (like surrounding it with fences) and is used to detect indexing arrays out of bounds. |
0xAB | (Allocated Block?) | Memory allocated by LocalAlloc(). |
0xBAADF00D | Bad Food | Memory allocated by LocalAlloc() with LMEM_FIXED, but not yet written to. |
0xCC | When the code is compiled with the /GZ option, uninitialized variables are automatically assigned to this value (at byte level). |
If you take a look at DBGHEAP.C, you can see how some of these values are defined:
static unsigned char _bNoMansLandFill = 0xFD; /* fill no-man's land with this */
static unsigned char _bDeadLandFill = 0xDD; /* fill free objects with this */
static unsigned char _bCleanLandFill = 0xCD; /* fill new objects with this */
Function | Description |
---|---|
malloc | C/C++ function that allocates a block of memory from the heap. The implementation of the C++ operator new is based on malloc. |
_malloc_dbg | Debug version of malloc; only available in the debug versions of the run-time libraries. _malloc_dbg is a debug version of the malloc function. When _DEBUG is not defined, each call to _malloc_dbg is reduced to a call to malloc. Both malloc and _malloc_dbg allocate a block of memory in the base heap, but _malloc_dbg offers several debugging features: buffers on either side of the user portion of the block to test for leaks, a block type parameter to track specific allocation types, and filename/linenumber information to determine the origin of allocation requests. |
free | C/C++ function that frees an allocated block. The implementation of C++ operator delete is based on free. |
_free_dbg | Debug version of free; only available in the debug versions of the run-time libraries. The _free_dbg function is a debug version of the free function. When _DEBUG is not defined, each call to _free_dbg is reduced to a call to free. Both free and _free_dbg free a memory block in the base heap, but _free_dbg accommodates two debugging features: the ability to keep freed blocks in the heap's linked list to simulate low memory conditions and a block type parameter to free specific allocation types. |
LocalAlloc GlobalAlloc | Win32 API to allocate the specified number of bytes from the heap. Windows memory management does not provide a separate local heap and global heap. |
LocalFree GlobalFree | Win32 API free the specified local memory object and invalidates its handle. |
HeapAlloc | Win32 API allocates a block of memory from a heap. The allocated memory is not movable. |
HeapFree | Win32 API frees a memory block allocated from a heap by the HeapAlloc or HeapReAlloc function. |
Note: Because this article is about memory management in a debug build, all the references to malloc and free in the following are actually references to their debug versions, _malloc_dbg and _free_dbg.
int main(int argc, char* argv[])
{
char *buffer = new char[12];
delete [] buffer;
return 0;
}
#define nNoMansLandSize 4} _CrtMemBlockHeader;
typedef struct _CrtMemBlockHeader
{
struct _CrtMemBlockHeader * pBlockHeaderNext;
struct _CrtMemBlockHeader * pBlockHeaderPrev;
char * szFileName;
int nLine;
size_t nDataSize;
int nBlockUse;
long lRequest;
unsigned char gap[nNoMansLandSize];
/* followed by:
* unsigned char data[nDataSize];
* unsigned char anotherGap[nNoMansLandSize];
*/
It stores the following information:
Field | Description |
---|---|
pBlockHeaderNext | A pointer to the next block allocated, but next means the previous allocated block because the list is seen as a stack, with the latest allocated block at the top. |
pBlockHeaderPrev | A pointer to the previous block allocated; this means the block that was allocated after the current block. |
szFileName | A pointer to the name of the file in which the call to malloc was made, if known. |
nLine | The line in the source file indicated by szFileName at which the call to malloc was made, if known. |
nDataSize | Number of bytes requested |
nBlockUse | 0 - Freed block, but not released back to the Win32 heap 1 - Normal block (allocated with new/malloc) 2 - CRT blocks, allocated by CRT for its own use |
lRequest | Counter incremented with each allocation |
gap | A zone of 4 bytes (in the current implementation) filled with 0xFD, fencing the data block, of nDataSize bytes. Another block filled with 0xFD of the same size follows the data. |
blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize;
Address | after HeapAlloc() | after malloc() |
---|---|---|
00320FD8 00320FDC 00320FE0 00320FE4 00320FE8 00320FEC 00320FF0 00320FF4 00320FF8 00320FFC 00321000 00321004 00321008 0032100C 00321010 00321014 00321018 0032101C 00321020 00321024 00321028 0032102C | 09 00 09 01 E8 07 18 00 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA AB AB AB AB AB AB AB AB 00 00 00 00 00 00 00 00 79 00 09 00 EE 04 EE 00 40 05 32 00 40 05 32 00 | 09 00 09 01 E8 07 18 00 98 07 32 00 00 00 00 00 00 00 00 00 00 00 00 00 0C 00 00 00 01 00 00 00 2E 00 00 00 FD FD FD FD CD CD CD CD CD CD CD CD CD CD CD CD FD FD FD FD AB AB AB AB AB AB AB AB 00 00 00 00 00 00 00 00 79 00 09 00 EE 04 EE 00 40 05 32 00 40 05 32 00 |
- Green: win32 bookkeeping info
- Blue: block size requested by malloc and filled with bad food
- Magenta: _CrtMemBlockHeader block
- Red: no mans land
- Black: requested data block
In this example, after the call to malloc() returns, buffer will point to memory address 0x00321000.
Address | Before HeapFree() | After HeapFree() |
---|---|---|
00320FD8 00320FDC 00320FE0 00320FE4 00320FE8 00320FEC 00320FF0 00320FF4 00320FF8 00320FFC 00321000 00321004 00321008 0032100C 00321010 00321014 00321018 0032101C 00321020 00321024 00321028 0032102C | 09 00 09 01 5E 07 18 00 DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD AB AB AB AB AB AB AB AB 00 00 00 00 00 00 00 00 79 00 09 00 EE 04 EE 00 40 05 32 00 40 05 32 00 | 82 00 09 01 5E 04 18 00 E0 2B 32 00 78 01 32 00 EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE EE FE |
- Green: win32 bookkeeping info
- Blue: CRT block filled with dead memory
- Gray: memory given back to win32 heap
The two tables above are put in a single, more detailed, table below:
Address (hex) | Offset | HeapAlloc | malloc | Free before HeapFree | Free after HeapFree | Description |
---|---|---|---|---|---|---|
00320FD8 | -40 | 01090009 | 01090009 | 01090009 | 01090082 | Win32 Heap info |
00320FDC | -36 | 001807E8 | 001807E8 | 0018075E | 0018045E | Win32 Heap info |
00320FE0 | -32 | BAADF00D | 00320798 | DDDDDDDD | 00322BE0 | pBlockHeaderNext |
00320FE4 | -28 | BAADF00D | 00000000 | DDDDDDDD | 00320178 | pBlockHeaderPrev |
00320FE8 | -24 | BAADF00D | 00000000 | DDDDDDDD | FEEEEEEE | szFileName |
00320FEC | -20 | BAADF00D | 00000000 | DDDDDDDD | FEEEEEEE | nLine |
00320FF0 | -16 | BAADF00D | 0000000C | DDDDDDDD | FEEEEEEE | nDataSize |
00320FF4 | -12 | BAADF00D | 00000001 | DDDDDDDD | FEEEEEEE | nBlockUse |
00320FF8 | -8 | BAADF00D | 0000002E | DDDDDDDD | FEEEEEEE | lRequest |
00320FFC | -4 | BAADF00D | FDFDFDFD | DDDDDDDD | FEEEEEEE | gap (no mans land) |
00321000 | 0 | BAADF00D | CDCDCDCD | DDDDDDDD | FEEEEEEE | Data requested |
00321004 | +4 | BAADF00D | CDCDCDCD | DDDDDDDD | FEEEEEEE | Data requested |
00321008 | +8 | BAADF00D | CDCDCDCD | DDDDDDDD | FEEEEEEE | Data requested |
0032100C | +12 | BAADF00D | FDFDFDFD | DDDDDDDD | FEEEEEEE | No mans land |
00321010 | +16 | ABABABAB | ABABABAB | ABABABAB | FEEEEEEE | Win32 Heap info |
00321014 | +20 | ABABABAB | ABABABAB | ABABABAB | FEEEEEEE | Win32 Heap info |
00321018 | +24 | 00000000 | 00000000 | 00000000 | FEEEEEEE | Win32 Heap info |
0032101C | +28 | 00000000 | 00000000 | 00000000 | FEEEEEEE | Win32 Heap info |
00321020 | +32 | 00090079 | 00090079 | 00090079 | FEEEEEEE | Win32 Heap info |
00321024 | +36 | 00EE04EE | 00EE04EE | 00EE04EE | FEEEEEEE | Win32 Heap info |
00321028 | +40 | 00320540 | 00320540 | 00320540 | FEEEEEEE | Win32 Heap info |
0032102C | +44 | 00320540 | 00320540 | 00320540 | FEEEEEEE | Win32 Heap info |