Albert Herrero
Albert Herrero

Reputation: 125

Strange behavior in 'malloc_stats' function

I'm doing some tests with 'malloc_stats' function and I've seen a strange behavior that I don't understand. The test is very easy, what I'm doing is allocate memory and print the 'malloc_stats' before the allocation, after the allocation and after free the memory. This is the code I'm using:

int main(int argc, char *argv[])
{
   char *alloc[MAX_ALLOCS];
   if ( argc < 3 or strcmp(argv[1], "--help") == 0 ) {
       cout << argv[0] << " num-blocks block-size [free-step [start-free [end-free]]]" << endl;
        return 1;
    }

   int numBlocks = atoi(argv[1]);
   size_t blockSize = atoi(argv[2]);
   int freeStep = (argc > 3) ? atoi(argv[3]) : 1;
   int freeBegin = (argc > 4) ? atoi(argv[4]) : 0;
   int freeEnd = (argc > 5) ? atoi(argv[5]) : numBlocks;

   cout << "============== Before allocating blocks ==============" << endl;
   malloc_stats();   

   for (int j = 0; j < numBlocks; j++)
   {
       alloc[j] = (char*) malloc(blockSize);
       if (alloc[j] == NULL) {
           cout << "ERROR: malloc" << endl;
           return 1;
       }
   }

   cout << endl << "============== After allocating blocks ==============" << endl;
   malloc_stats();   

   for (int j = freeBegin; j < freeEnd; j += freeStep) {
       free(alloc[j]);
   }

   cout << endl << "============== After freeing blocks ==============" << endl;
   malloc_stats();   

   return 1;
}

And this is the output I'm getting:

./exe 1000 100 1
============== Before allocating blocks ==============
Arena 0:
system bytes     =     135168
in use bytes     =      74352
Total (incl. mmap):
system bytes     =     135168
in use bytes     =      74352
max mmap regions =          0
max mmap bytes   =          0

============== After allocating blocks ==============
Arena 0:
system bytes     =     270336
in use bytes     =     186352
Total (incl. mmap):
system bytes     =     270336
in use bytes     =     186352
max mmap regions =          0
max mmap bytes   =          0

============== After freeing blocks ==============
Arena 0:
system bytes     =     270336
in use bytes     =      75136
Total (incl. mmap):
system bytes     =     270336
in use bytes     =      75136
max mmap regions =          0
max mmap bytes   =          0

At this point, if I compare the "in use bytes" before the allocation and after the freeing, there is a difference of 784 bytes.

I cannot understand what is happening, I supposed that the 'in use bytes' had to be the same... where are these bytes?

Thank you.

Upvotes: 8

Views: 2462

Answers (1)

eozd
eozd

Reputation: 1213

Short Answer

This discrepancy in the number of bytes is related to paging used by the operating systems, and is not a memory leak. When you allocate block sizes that are not multiple of the page size, some of the extra memory allocated are not freed. This extra memory can later be used by the allocator, and is not leaked.

Long Answer

If we check the manual page of the malloc_stats function [1], we see that the total number of bytes consumed by in-use allocations is obtained by the uordblks field of the mallinfo struct [2] whose documentation says that uordblks is the number of total allocated bytes. Here, allocated does not necessarily mean that all of this memory is used by you as the application programmer. Allocator may allocate more memory than was requested for alignment purposes if it leads to higher performance and efficiency.

This is the issue described in your question. You run your program as

./exe 1000 100 1

where 1000 is the number of blocks and 100 is the block size. Since 100 is not a multiple of the page size used by the operating system, malloc will tend to allocate more memory than you request on each call.

As a quick check to see that this is indeed the case, we can allocate block sizes that are equal to the page size of our operating system. On Linux distributions, page size can be obtained by the following shell command:

getconf PAGE_SIZE

which returns 4096 on my case. When I run your program as

./exe 100 4096 1

I get the following as output: (note that the in-use bytes after the deallocation is finished is the same as the in-use bytes before the allocation)

============== Before allocating blocks ==============
Arena 0:
system bytes     =     135168
in use bytes     =      74352
Total (incl. mmap):
system bytes     =     135168
in use bytes     =      74352
max mmap regions =          0
max mmap bytes   =          0

============== After allocating blocks ==============
Arena 0:
system bytes     =     540672
in use bytes     =     485552
Total (incl. mmap):
system bytes     =     540672
in use bytes     =     485552
max mmap regions =          0
max mmap bytes   =          0

============== After freeing blocks ==============
Arena 0:
system bytes     =     208896
in use bytes     =      74352
Total (incl. mmap):
system bytes     =     208896
in use bytes     =      74352
max mmap regions =          0
max mmap bytes   =          0

References

Upvotes: 8

Related Questions