Skip to content
Advertisement

Understanding glibc malloc trimming

Some program that I am currently working on consumes much more memory than I think it should. So I am trying to understand how glibc malloc trimming works. I wrote the following test:

JavaScript

Test output (without calling malloc_trim):

JavaScript

Even though almost all memory was released, this test code consumes much more resident memory than expected:

JavaScript

Process smaps:

JavaScript

When I enable the call to malloc_trim the output of the test stays almost the same:

JavaScript

However, the RSS decreases significantly:

JavaScript

Process smaps (after malloc_trim):

JavaScript

After calling malloc_trim, the heap got shunked. I assume the 8MB mmap segment is still available because of the last piece of memory which wasn’t released.

Why heap trimming isn’t performed automatically by malloc? Is there a way to configure malloc such that trimming will be done automatically (when it can save that much of a memory)?

I am using glibc version 2.17.

Advertisement

Answer

Largely for historical reasons, memory for small allocations comes from a pool managed with the brk system call. This is a very old system call — at least as old as Version 6 Unix — and the only thing it can do is change the size of an “arena” whose position in memory is fixed. What that means is, the brk pool cannot shrink past a block that is still allocated.

Your program allocates N blocks of memory and then deallocates N-1 of them. The one block it doesn’t deallocate is the one located at the highest address. That is the worst-case scenario for brk: the size can’t be reduced at all, even though 99.99% of the pool is unused! If you change your program so that the block it doesn’t free is array[0] instead of array[NUM_CHUNKS-1], you should see both RSS and address space shrink upon the final call to free.

When you explicitly call malloc_trim, it attempts to work around this limitation using a Linux extension, madvise(MADV_DONTNEED), which releases the physical RAM, but not the address space (as you observed). I don’t know why this only happens upon an explicit call to malloc_trim.

Incidentally, the 8MB mmap segment is for your initial allocation of array.

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement