10

I'm experimenting with a simple C program that keeps allocating memory:

for ( i = 0; i < 10000; i ++ )
{
    array[i] = malloc( 1024*1024 );
    if ( array[i] == NULL )
    {
        break;
    }
    printf("Sleeping...%d\n", i);
    sleep( 60 );
}

Full code pasted here: http://tny.cz/2d9cb3df

However, when I cat /proc/pid/maps, I get no [heap] section. Why?

08048000-08049000 r-xp 00000000 08:11 17         /data/a.out
08049000-0804a000 r--p 00000000 08:11 17         /data/a.out
0804a000-0804b000 rw-p 00001000 08:11 17         /data/a.out
0804b000-0805e000 rw-p 00000000 00:00 0 
b74c6000-b75c8000 rw-p 00000000 00:00 0 
b75c8000-b7775000 r-xp 00000000 08:01 3539272    /lib/i386-linux-gnu/libc-2.17.so
b7775000-b7777000 r--p 001ad000 08:01 3539272    /lib/i386-linux-gnu/libc-2.17.so
b7777000-b7778000 rw-p 001af000 08:01 3539272    /lib/i386-linux-gnu/libc-2.17.so
b7778000-b777b000 rw-p 00000000 00:00 0 
b7797000-b779a000 rw-p 00000000 00:00 0 
b779a000-b779b000 r-xp 00000000 00:00 0          [vdso]
b779b000-b77bb000 r-xp 00000000 08:01 3539289    /lib/i386-linux-gnu/ld-2.17.so
b77bb000-b77bc000 r--p 0001f000 08:01 3539289    /lib/i386-linux-gnu/ld-2.17.so
b77bc000-b77bd000 rw-p 00020000 08:01 3539289    /lib/i386-linux-gnu/ld-2.17.so
bff21000-bff42000 rw-p 00000000 00:00 0          [stack]
lang2
  • 11,433
  • 18
  • 83
  • 133
  • The compiler deduces that the for loop doesn't do anything (i. e. no-op) and optimizes it out? –  Jul 22 '13 at 08:04
  • @H2CO3, I changed it to print out the allocated address and it's still the same. – lang2 Jul 22 '13 at 08:07
  • 1
    @H2CO3 That seems like a big leap even for an overly aggressive optimizing compiler, as the loop contains three calls to functions that have quite big side-effects. – Some programmer dude Jul 22 '13 at 08:12
  • @JoachimPileborg Well, yes, fair enough. (But I've seen "worse" optimizations produced by GCC and clang as well...) –  Jul 22 '13 at 08:18
  • @Mat code link added. – lang2 Jul 22 '13 at 09:28

3 Answers3

10

Since you're talking about /proc/pid/maps I assume you're on linux.

This is what man malloc tells me on the linux distribution I happen to run:

Normally, malloc() allocates memory from the heap, and adjusts the size of the heap as required, using sbrk(2).

When allocating blocks of memory larger than MMAP_THRESHOLD bytes, the glibc malloc() implementation allocates the memory as a private anonymous mapping using mmap(2). MMAP_THRESHOLD is 128 kB by default, but is adjustable using mallopt(3).

Allocations performed using mmap(2) are unaffected by the RLIMIT_DATA resource limit (see getrlimit(2)).

If you really want to see [heap] in your /proc/pid/maps allocate less than 128k per call to malloc.

Community
  • 1
  • 1
Art
  • 19,807
  • 1
  • 34
  • 60
1

The said section is just not declared as "heap", as it is allocated via mmap() calls.

Here on my 64 bit system, the memory allocated comes from this constantly growing section:

7f7fbda7a000-7f7fbdc7c000 rw-p 00000000 00:00 0
...
7f7fbc868000-7f7fbdc7c000 rw-p 00000000 00:00 0
...
7f7fbc363000-7f7fbdc7c000 rw-p 00000000 00:00 0

The section is extended at the start and the newly allocated memory always ias the start address + 0x10.

What the memory allocator does here is

mmap(NULL, 1052672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7fbb353000

Strange thing; I would have suspected that every mmap()ed memory section is shown separately. But if I have the program do intermediate free() calls, the memory allocation follows this:

7f16be944000-7f16bec47000 rw-p 00000000 00:00 0
7f16bed48000-7f16bee49000 rw-p 00000000 00:00 0

There has been made a hole in the otherwise contiguous memory.

And an additional thing happens: in this modified program, there is indeed a [heap] section. Due to reasuns beyond my understanding, the program swithces over to allocating via brk(), and this is (probably) where the heap comes from:

with every brk() call, the respective section is extended at its end:

01183000-029a4000 rw-p 00000000 00:00 0                                  [heap]

I do not know what has changed malloc()'s mind to take the "real" heap (the stuff connected to brk()) for the allocations. The first munmap() call, related to a respective free(), seems to produce this.

According to this, there seems to be a maximum for mmap()ed chunks. If this is exceeded, gnulibc falls back to brk()/sbrk(), which operates on the "regular heap area".

So, shorter answer: The malloc()ed memory taken from via brk() is on the "real" [heap] section, the mmap()ed memory sections are not tagged as [heap].

glglgl
  • 89,107
  • 13
  • 149
  • 217
0

Dynamic allocations are made by using the brk() call. The start of the .brk section is calculated during program loading (look at load_elf_binary() in /linux/fs/binfmt_elf.c). However, a page is not mapped till during runtime when the actual allocation is requested. See below a screen show of the /proc/pid/maps before and after an allocation on an x86_64 system

Before invoking a calloc(8032,1):

$ cat /proc/$(pid)/maps

00400000-00401000 r-xp 00000000 08:02 1314308             /testheap

00600000-00601000 r--p 00000000 08:02 1314308             /testheap

00601000-00602000 rw-p 00001000 08:02 1314308             /testheap

7fafeea3e000-7fafeebfb000 r-xp 00000000 08:02 198420      /lib/x86_64-linux-gnu/libc-2.17.so

7fafeebfb000-7fafeedfb000 ---p 001bd000 08:02 198420      /lib/x86_64-linux-gnu/libc-2.17.so

After invoking a calloc(8032,1):

$cat /proc/$(pid)/maps

00400000-00401000 r-xp 00000000 08:02 1314308             /testheap

00600000-00601000 r--p 00000000 08:02 1314308             /testheap

00601000-00602000 rw-p 00001000 08:02 1314308             /testheap

**021ea000-0220c000 rw-p 00000000 00:00 0                   [heap]**

7fafeea3e000-7fafeebfb000 r-xp 00000000 08:02 198420      /lib/x86_64-linux-gnu/libc-2.17.so

7fafeebfb000-7fafeedfb000 ---p 001bd000 08:02 198420      /lib/x86_64-linux-gnu/libc-2.17.so

Ram
  • 3,092
  • 10
  • 40
  • 56
daemon155
  • 123
  • 1
  • 8