7

Here's my program

#include <vld.h>

using namespace std;

int main() {
    int* p = new int(100);
}

Visual Leak Detector Report

Visual Leak Detector Version 2.3 installed.
WARNING: Visual Leak Detector detected memory leaks!
---------- Block 1 at 0x00891B60: 4 bytes ----------
  Call Stack:
    c:\xxx\documents\visual studio 2010\projects\stl1\stl1\stl1.cpp (11): stl1.exe!main + 0x7 bytes
    f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (555): stl1.exe!__tmainCRTStartup + 0x19 bytes
    f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (371): stl1.exe!mainCRTStartup
    0x76B7338A (File and line number not available): kernel32.dll!BaseThreadInitThunk + 0x12 bytes
    0x774B97F2 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x63 bytes
    0x774B97C5 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x36 bytes
  Data:
    64 00 00 00                                                  d....... ........


Visual Leak Detector detected 1 memory leak (40 bytes).
Largest number used: 40 bytes.
Total allocations: 40 bytes.
Visual Leak Detector is now exiting.
The program '[8992] stl1.exe: Native' has exited with code 0 (0x0).

Why 40 bytes memory leak, it really should have been 4 bytes.

Can anyone explain what is going on here?

user1
  • 4,031
  • 8
  • 37
  • 66
  • 2
    There will be always a bigger block of memory allocated than initially requested. – πάντα ῥεῖ Oct 01 '15 at 10:50
  • 2
    @Robinson int* is int(100); and not int[100]. – user1 Oct 01 '15 at 10:52
  • 1
    Think about `X* p = new X[y]`, when you call `delete[] p`, all the `y` destructors will be called. However, delete only get a single pointer. How does it know about how long is the array ? Because it's stored in the allocated block which is way-larger than the memory required for the allocated objects. – xryl669 Oct 01 '15 at 11:08

2 Answers2

5

First, when you ask for 4 bytes allocation, chances are high that you'll always get a larger block (which is safe, because you are supposed to use only the 4 bytes you've asked for).

Why?

  1. Allocation size has to be stored somewhere (think about new X[count] case, and delete[] that must call count times the destructor of X

  2. Then, heap allocation is usually done by recursive fragmenting of the heap for example the Buddy_memory_allocation. This is because you want as low overhead as possible (that is, the amount of bytes used for managing allocations compared to the really allocated bytes). You need to remember if some block of memory is used or not.

  3. Debugging might also add allocation size. On Visual Studio, the malloc / free function insert 4 bytes before the returned pointer with a "memory guard" like (0xDDDDDDDD), and allocate 4 more bytes too for another memory guard after the requested size. When you call malloc or free (indirectly, new and delete), the heap handler's code checks the guard and assert that they are not modified. If they are, it stops your program so you can see "around" the place where the memory was modified. IIRC, 0xCDCDCDCD is used for filling allocated area, 0xFEEEFEEE is used to fill freed area but the block is not returned yet to the system, and 0xDDDDDDDD are used for boundaries.

So it seems that the size of the block you're receiving is 40 bytes (maybe more) even if you only use "4". VLD does not track your code, it intercept memory management functions (like malloc/free), and build a list of each block allocated. This list is parsed to remove elements when they are freed. Upon termination, any remaining item is listed.

So the malloc call received likely comes from a ::operator new which enlarged the requested size to 40 bytes, or maybe a block of 32 bytes is added in VLD to track the "allocation request".

After looking at VLD source code, specifically the vldnew function, it allocate a header for each allocation:

vldblockheader_t *header = (vldblockheader_t*)RtlAllocateHeap(g_vldHeap, 0x0, size + sizeof(vldblockheader_t))

Likely, the vldblockheader_t is 36 bytes in your case.

user1
  • 4,031
  • 8
  • 37
  • 66
xryl669
  • 3,376
  • 24
  • 47
  • Even if you consider debugging overhead, guard bytes, store allocation size; 40 bytes is way bigger for one byte consideration. 10x overhead is just too much! – user1 Oct 01 '15 at 11:24
  • 1
    I think that if you had a strace like equivalent on Windows, the actual block requested is likely 64 bytes or more not 40. However, if you allocate another `int`, the pointer returned will likely be in the same `2^n`-sized memory block so it'll cost 0 additional heap from the OS point of view. Memory allocation algorithms are often compared in amortized time, not for a single allocation. – xryl669 Oct 01 '15 at 11:28
  • Another note, but more general, is that allocating on the heap is more expensive than allocating on the stack. That's why it's recommended to use stack allocation whenever possible. – xryl669 Oct 01 '15 at 11:31
  • I added 2 new statements, `int* p1 = new int(100); int* p2 = new int(100);` and now, total memory leaked is 120 bytes. It is actually linear growth. `40 bytes * n allocations`. My statement still holds true - _40 bytes for one int*_ is just too much. You are missing some major details in your answer. – user1 Oct 01 '15 at 11:38
  • 2
    Ok, I've searched the VLD source code and it's indeed VLD overhead going on here. Please see my edit – xryl669 Oct 01 '15 at 11:58
0

Why 40 bytes memory leak, it really should have been 4 bytes.

It has to do both with additional information about the dynamically allocated object and the efficient1, 2 management of the dynamical(heap) memory.

Regarding the former, there should be information available so that the allocated heap memory is freed after the object life ends.

As for the latter, there is something called capacity and it is not necessarily to be equal to the allocated size. It can be equal or greater, with the extra space allowing to accommodate for growth without the need to reallocate on each insertion.

Notice that this capacity does not suppose a limit on the size of the type, in your case int.

Example:

vectors are sequence containers representing arrays that can change in size. Internally, vectors use a dynamically allocated array to store their elements. A call to the following three vector member functions:

  • size()

  • max_size()

  • capacity()

will return different values and will provide you with some insight of the strategy used when allocating heap memory.


1. If the initially allocated object needs to grow it may be necessary to be entirely reallocated, instead of expanded to neighbouring/contiguous memory section. Involving a lot of operations.

2. Extra padding could be added for memory alignment (multiples of 4 bytes, so that it can be read with a less memory accesses)

Ziezi
  • 6,375
  • 3
  • 39
  • 49