Does free() unmap the memory of a process?

10,003

The C-library function free() can, but does not have to, return memory to the kernel.

Some implementations of malloc() move the boundary between "heap" and otherwise unused address space (the "system break") via the sbrk() system call, then dole out smaller pieces of those large allocations. Without getting every smaller piece de-allocated, free() can't really return the memory to the OS.

That same reason applies to malloc() implementations that don't use sbrk(2), but maybe use mmap("/dev/zero") or something.. I can't find a reference, but I seem to remember that one or another of the BSD's used mmap() that way to get pages of memory. Nevertheless, free() can't return a page to the operating system unless every sub-allocation is deallocated by the program.

Some malloc() implementations do return memory to the system: ChorusOS(?) apparently did. It's not clear if it moved the system break, or munmap()'ed pages.

Here's a paper about a memory allocator that improves performance by "aggressively giving up free pages to the virtual memory manager". Slide show for a talk about the allocator.

Share:
10,003

Related videos on Youtube

michelle
Author by

michelle

Updated on September 18, 2022

Comments

  • michelle
    michelle over 1 year

    I am running a C program on Linux 2.6.16 kernel. I do not think there are memory leaks in my program however the memory consumption for the program remains stable after certain operations and does not decrease. I use the 'ps v ' command to monitor the RSS value of my program.

    The valgrind massif tool shows a large portion of heap is allocated by mmap in my process. But according to the code those allocations should have been freed after the operations are done. Is it because the freed memory is still mapped and/or still contributes to the RSS value of the process?

    Any insight will be very appreciated!

    Below is the snip from the valgrind massif report. Note I have turned on the --pages-as-heap option for the massif tool to measure all memories used by the program.

    --------------------------------------------------------------------------------
      n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
    --------------------------------------------------------------------------------
     85 701,483,989,262      173,576,192      173,576,192             0            0
     86 704,352,949,469      173,367,296      173,367,296             0            0
     87 707,582,275,643      173,367,296      173,367,296             0            0
     88 710,536,145,814      173,367,296      173,367,296             0            0
    100.00% (173,367,296B) (page allocation syscalls) mmap/mremap/brk, --alloc-fns, etc.
    ->53.40% (92,581,888B) 0x649248B: mmap (in /lib64/tls/libc.so.6)
    | ->41.13% (71,303,168B) 0x6446D85: _int_malloc (in /lib64/tls/libc.so.6)
    | | ->39.31% (68,157,440B) 0x6448D62: calloc (in /lib64/tls/libc.so.6)
    ......[my own functions are omitted]
    ->35.28% (61,157,376B) 0x400F51B: mmap (in /lib64/ld-2.3.3.so)
    | ->28.81% (49,954,816B) 0x4004CE8: _dl_map_object_from_fd (in /lib64/ld-2.3.3.so)
    | | ->28.81% (49,954,816B) 0x400636B: _dl_map_object (in /lib64/ld-2.3.3.so)
    | |   ->18.89% (32,755,712B) 0x400AB42: openaux (in /lib64/ld-2.3.3.so)
    | |   | ->18.89% (32,755,712B) 0x400AF7C: _dl_catch_error (in /lib64/ld-2.3.3.so)
    | |   |   ->18.89% (32,755,712B) 0x4009FCF: _dl_map_object_deps (in /lib64/ld-2.3.3.so)
    | |   |     ->18.89% (32,755,712B) 0x40021FD: dl_main (in /lib64/ld-2.3.3.so)
    | |   |       ->18.89% (32,755,712B) 0x400E7F6: _dl_sysdep_start (in /lib64/ld-2.3.3.so)
    | |   |         ->18.89% (32,755,712B) 0x4001477: _dl_start (in /lib64/ld-2.3.3.so)
    | |   |           ->18.89% (32,755,712B) 0x4000CF6: ??? (in /lib64/ld-2.3.3.so)
    | |   |             ->18.89% (32,755,712B) 0x0: ???
    | |   |               ->18.89% (32,755,712B) 0x7FF0003D5: ???
    | |   |                 ->18.89% (32,755,712B) 0x7FF0003E4: ???
    | |   |
    ......
    
    • Mikel
      Mikel over 11 years
      Why aren't you using munmap? munmap(2)
    • michelle
      michelle over 11 years
      We do not use munmap since glibc malloc and free has the implementations. Yes shared libraries are used. Why does that matter? The relevant parts of valgrind report is added to the main question.
    • Gilles 'SO- stop being evil'
      Gilles 'SO- stop being evil' over 11 years
    • Mikel
      Mikel over 11 years
      @michelle I thought you meant you were calling mmap. But now I think I understand: You're calling malloc/calloc, and it's calling mmap?
    • michelle
      michelle over 11 years
      Yes. the --pages-as-heap option for massif is set to yes to measure all the memory used by the program. Massif's normal heap block profiling is replaced by lower-level page profiling.
    • michelle
      michelle over 11 years
      Is it true that a mmap'ed page for a process can be used by multiple malloc/calloc for different variables? If yes then it makes sense that when some variables are freed but not others then the page is still mapped for the process. Therefore valgrind still counts all variables of that page in its memory measurement?
  • michelle
    michelle over 11 years
    I understand free() does not return memories to OS. But does free() unmap the memory or does the memory remain to be mapped by the glibc memory allocator?
  • michelle
    michelle over 11 years
    "free() can't return a page to the operating system unless every sub-allocation is deallocated by the program." I do not really understand this. Could you explain 'sub-allocation'?
  • Admin
    Admin over 11 years
    @michelle: suppose a page size of 4192 bytes. A program calls malloc(4186) and then malloc(8). The program has allocated all of a page. The program calls free() on the 4186-byte allocation. free() cannot unmap the page because the 8-byte-allocation has not been free()'ed.
  • michelle
    michelle over 11 years
    That makes sense. Thanks Bruce. So my guess is when the page is still mapped, all data in it are still taken into consideration by valgrind measurement though some are already freed. Correct?