Visual Leak Detector reports 40 bytes leaked for one int*
Asked Answered
B

2

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?

Bryant answered 1/10, 2015 at 10:41 Comment(3)
There will be always a bigger block of memory allocated than initially requested.Supercharge
@Robinson int* is int(100); and not int[100].Bryant
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.Gallinacean
G
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.

Gallinacean answered 1/10, 2015 at 11:20 Comment(5)
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!Bryant
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.Gallinacean
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.Gallinacean
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.Bryant
Ok, I've searched the VLD source code and it's indeed VLD overhead going on here. Please see my editGallinacean
G
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)

Godolphin answered 1/10, 2015 at 13:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.