The Windows Heap never shrinks

The Windows Heap manager is used to allocate/de-allocate relatively small size of memory blocks. It has an interesting (and sometimes annoying) characteristics – the heap never shrinks.

Look at the code below. It allocates a lot of 480KB memory blocks and then releases them.

using namespace std;

int main(){
vector< void* > blockList;

for(size_t i = 0; i < 4096; i++){
void* block = malloc(480 * 1024);
blockList.push_back(block);
}

for(vector< void* >::iterator it = blockList.begin(); it != blockList.end(); ++it){
free(*it);
}
OutputDebugString("OCOMMAND: !address -summaryn");
return 0;
}

At the time of OutputDebugString(), the memory usage should go back to the state of program start. However, it does not. Let’s take a look at the !address -summary output.

——————– Usage SUMMARY ————————–
    TotSize (      KB)   Pct(Tots) Pct(Busy)   Usage
     22b000 (    2220) : 00.11%    00.11%    : RegionUsageIsVAD
    48e1000 (   74628) : 03.56%    00.00%    : RegionUsageFree
     326000 (    3224) : 00.15%    00.16%    : RegionUsageImage
     100000 (    1024) : 00.05%    00.05%    : RegionUsageStack
       1000 (       4) : 00.00%    00.00%    : RegionUsageTeb
   7b0b9000 ( 2015972) : 96.13%    99.68%    : RegionUsageHeap
          0 (       0) : 00.00%    00.00%    : RegionUsagePageHeap
       1000 (       4) : 00.00%    00.00%    : RegionUsagePeb
       1000 (       4) : 00.00%    00.00%    : RegionUsageProcessParametrs
       2000 (       8) : 00.00%    00.00%    : RegionUsageEnvironmentBlock
       Tot: 7fff0000 (2097088 KB) Busy: 7b70f000 (2022460 KB)

——————– Type SUMMARY ————————–
    TotSize (      KB)   Pct(Tots)  Usage
    48e1000 (   74628) : 03.56%   : <free>
     326000 (    3224) : 00.15%   : MEM_IMAGE
     21a000 (    2152) : 00.10%   : MEM_MAPPED
   7b1cf000 ( 2017084) : 96.18%   : MEM_PRIVATE

——————– State SUMMARY ————————–
    TotSize (      KB)   Pct(Tots)  Usage
    3dac000 (   63152) : 03.01%   : MEM_COMMIT
    48e1000 (   74628) : 03.56%   : MEM_FREE
   77963000 ( 1959308) : 93.43%   : MEM_RESERVE

Largest free region: Base 7c9af000 – Size 02d41000 (46340 KB)

 

The heap usage (RegionUsageHeap) is almost 2GB. The largest free region is just 45MB.

When you allocate a memory block through heap manager (malloc, new, HeapAlloc, GlobalAlloc, etc), depending on the allocation size, it’s allocated in two different locations. When the allocation size is less than certain threshold (typically, 512KB), it’s allocated inside the heap manager. Otherwise, it’s allocated through VirtualAlloc(). The allocation inside the heap manager is quite efficient in terms of speed and memory usage. The VirtualAlloc() is slower and less efficient.

However, the memory blocks inside the heap manager are never released even if you call free(). The memory block is de-committed and becomes RESERVED range, but it still consumes process address space. To really release the region, you have to destroy the heap manager with HeapDestroy(). The sample program above showed this problem.

It’s not an academic problem. I’ve seen this problem in some video/audio codec implementations including QuickTime.

Generally speaking, If you allocate a lot of small (less than 512KB) blocks so that total memory size is big enough to be a problem, you may want to consider these alternatives.

  • Create a private heap manager with HeapCreate(), and destroy it after use.
  • Use VirtualAlloc() instead. Memory blocks allocated by VirtualAlloc() are certainly released on VirtualFree() function.

Advertisements

About Moto

Engineer who likes coding
This entry was posted in Windows Memory Management. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s