Will malloc implementations return free-ed memory back to the system?
Asked Answered
I

8

71

I have a long-living application with frequent memory allocation-deallocation. Will any malloc implementation return freed memory back to the system?

What is, in this respect, the behavior of:

  • ptmalloc 1, 2 (glibc default) or 3
  • dlmalloc
  • tcmalloc (google threaded malloc)
  • solaris 10-11 default malloc and mtmalloc
  • FreeBSD 8 default malloc (jemalloc)
  • Hoard malloc?

Update

If I have an application whose memory consumption can be very different in daytime and nighttime (e.g.), can I force any of malloc's to return freed memory to the system?

Without such return freed memory will be swapped out and in many times, but such memory contains only garbage.

Incrassate answered 6/2, 2010 at 23:58 Comment(14)
You're thinking free() might be a practical joke being played on you?Ochrea
Swapped out memory won't be swapped in until needed. If you have enough swap space to accommodate the "freed" memory, or can increase swap to that level, there is no problem with any malloc.Mb
I don't want system to use swap for "freed" memory. Much cheaper and faster is to return this memory to system and don't ever try to put it on disk. Why I need to fill swap with garbage?Incrassate
It's actually much cheaper and faster to use existing solutions and get my real work done. For example, I have a 1TB drive and 0.3% of that (3GB) reserved for swap (and even still, it's rare to see 500MB of swap used). Can you allocate 1% of your drive? That's about $1-5 in hardware.Mb
I dont want my system to do swapping. I can do everything, but I want not to swap to disk and back a garbage. It make noise, and eats hard drive "seeks per second" capacity. Swapping of garbage is UNNEEDED operation, ever if it costs 2 cents, it must be eliminated.Incrassate
This is actually very important for embedded or other diskless systems that don't use swap.Wet
+1 to OP and I wish I could give -1 to all the detractors saying swap will take care of it. This kind of careless attitude towards swap and virtual memory is why modern Linux desktops spend half of their time chugging at the hard disk just like Windows...Technicolor
I really detest the attitude of of the first comment - the fact is that most malloc implementations will not release memory to the operating system, and the few that do, do not do it very easily. I guess free() is actually a practical joke played on GregS, instead of the original poster.Arielle
The jemalloc documentation <facebook.com/notes/facebook-engineering/…> explicitly states that it will aggressively return memory to the OS. Apparently this is easier on BSD than on Linux, since BSD has a special system call to indicate that the OS can take memory away from the application if needed.Chronicles
This question really doesn't make sense. On modern operating systems, the malloc and free functions only allocate address space in the process. And there's no point in returning that to the operating system -- that doesn't even really mean anything. And physical memory can be reclaimed by the operating system without it having to be given back.Tabular
@Incrassate Fragmentation and returning memory back to the system have almost nothing to do with each other. You can return 99% of the memory back to the system and still have awful fragmentation. You can return no memory back to the system and have no fragmentation. Fragmentation is about the pattern of memory that is in use, not the amount not returned.Tabular
@Incrassate I think you're doing a lot of different things and seeing a different result, but most likely wrong about which differences matter. For example, MADV_DONTNEED has no effect on fragmentation.Tabular
@Incrassate I understand all of that. But you seem to think that fragmentation has something to do with returning memory to the OS. They are entirely unrelated. Fragmentation occurs in the process' virtual address space and can occur precisely the same whether the gaps between the used areas have been returned to the OS or not.Tabular
I found this answer to be very good and related.Myriam
K
42

The following analysis applies only to glibc (based on the ptmalloc2 algorithm). There are certain options that seem helpful to return the freed memory back to the system:

  1. mallopt() (defined in malloc.h) does provide an option to set the trim threshold value using one of the parameter option M_TRIM_THRESHOLD, this indicates the minimum amount of free memory (in bytes) allowed at the top of the data segment. If the amount falls below this threshold, glibc invokes brk() to give back memory to the kernel.

    The default value of M_TRIM_THRESHOLD in Linux is set to 128K, setting a smaller value might save space.

    The same behavior could be achieved by setting trim threshold value in the environment variable MALLOC_TRIM_THRESHOLD_, with no source changes absolutely.

    However, preliminary test programs run using M_TRIM_THRESHOLD has shown that even though the memory allocated by malloc does return to the system, the remaining portion of the actual chunk of memory (the arena) initially requested via brk() tends to be retained.

  2. It is possible to trim the memory arena and give any unused memory back to the system by calling malloc_trim(pad) (defined in malloc.h). This function resizes the data segment, leaving at least pad bytes at the end of it and failing if less than one page worth of bytes can be freed. Segment size is always a multiple of one page, which is 4,096 bytes on i386.

    The implementation of this modified behavior of free() using malloc_trim could be done using the malloc hook functionality. This would not require any source code changes to the core glibc library.

  3. Using madvise() system call inside the free implementation of glibc.

Kob answered 5/10, 2010 at 2:35 Comment(1)
Also checkout M_MMAP_THRESHOLD, which is the threshold above which malloc() uses mmap() to obtain memory. Apparently if you free blocks exceeding this size, you return the memory to the operating system.Danyelledanyette
S
19

Most implementations don't bother identifying those (relatively rare) cases where entire "blocks" (of whatever size suits the OS) have been freed and could be returned, but there are of course exceptions. For example, and I quote from the wikipedia page, in OpenBSD:

On a call to free, memory is released and unmapped from the process address space using munmap. This system is designed to improve security by taking advantage of the address space layout randomization and gap page features implemented as part of OpenBSD's mmap system call, and to detect use-after-free bugs—as a large memory allocation is completely unmapped after it is freed, further use causes a segmentation fault and termination of the program.

Most systems are not as security-focused as OpenBSD, though.

Knowing this, when I'm coding a long-running system that has a known-to-be-transitory requirement for a large amount of memory, I always try to fork the process: the parent then just waits for results from the child [[typically on a pipe]], the child does the computation (including memory allocation), returns the results [[on said pipe]], then terminates. This way, my long-running process won't be uselessly hogging memory during the long times between occasional "spikes" in its demand for memory. Other alternative strategies include switching to a custom memory allocator for such special requirements (C++ makes it reasonably easy, though languages with virtual machines underneath such as Java and Python typically don't).

Supposal answered 7/2, 2010 at 0:26 Comment(4)
Can I use fork in multithreaded app? So I really CANT use fork.Incrassate
@osgx: Yes you can can fork in a multithreaded application as long as you only use it to exec a new process. Well, actually "... the child process may only execute async-signal-safe operations until such time as one of the exec functions is called"Illhumored
@Zan: Where did you get that idea? fork is allowed in multi-threaded processes, and you can do whatever you like as long as you don't corrupt the state of your own synchronization objects by using it. pthread_atfork gives you the tools to avoid doing so.Technicolor
@R. POSIX. Where did you get your idea?Illhumored
C
7

I had a similar problem in my app, after some investigation I noticed that for some reason glibc does not return memory to the system when allocated objects are small (in my case less than 120 bytes).
Look at this code:

#include <list>
#include <malloc.h>

template<size_t s> class x{char x[s];};

int main(int argc,char** argv){
    typedef x<100> X;

    std::list<X> lx;
    for(size_t i = 0; i < 500000;++i){
        lx.push_back(X());
    }

    lx.clear();
    malloc_stats();

    return 0;
}

Program output:

Arena 0:
system bytes     =   64069632
in use bytes     =          0
Total (incl. mmap):
system bytes     =   64069632
in use bytes     =          0
max mmap regions =          0
max mmap bytes   =          0

about 64 MB are not return to system. When I changed typedef to: typedef x<110> X; program output looks like this:

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

almost all memory was freed. I also noticed that using malloc_trim(0) in either case released memory to system.
Here is output after adding malloc_trim to the code above:

Arena 0:
system bytes     =       4096
in use bytes     =          0
Total (incl. mmap):
system bytes     =       4096
in use bytes     =          0
max mmap regions =          0
max mmap bytes   =          0
Chimborazo answered 15/12, 2013 at 18:44 Comment(2)
Since 2008 glibc's ptmalloc malloc_trim does iterate over all memory and release fully free aligned 4k pages back to OS with MADV_DONTNEED: https://mcmap.net/q/280957/-force-free-to-return-malloc-memory-back-to-os. This is partly documented in man page man7.org/linux/man-pages/man3/malloc_trim.3.html (since glibc 2.8 or 2.9). Try tcmalloc or jemalloc, they usually return freed memory back to OS better than glibc's ptmallocIncrassate
This is possibly due to this bug with fast bins: sourceware.org/bugzilla/show_bug.cgi?id=14827Syphilology
V
6

I am dealing with the same problem as the OP. So far, it seems possible with tcmalloc. I found two solutions:

  1. compile your program with tcmalloc linked, then launch it as :

    env TCMALLOC_RELEASE=100 ./my_pthread_soft
    

    the documentation mentions that

    Reasonable rates are in the range [0,10].

    but 10 doesn't seem enough for me (i.e I see no change).

  2. find somewhere in your code where it would be interesting to release all the freed memory, and then add this code:

    #include "google/malloc_extension_c.h" // C include
    #include "google/malloc_extension.h"   // C++ include
    
    /* ... */
    
    MallocExtension_ReleaseFreeMemory();
    

The second solution has been very effective in my case; the first would be great but it isn't very successful, it is complicated to find the right number for example.

Vetiver answered 10/3, 2010 at 15:10 Comment(2)
Thanks, sounds rather interesting.Incrassate
For tcmalloc 1.7 this page says that it will not return any memory back to the systemIncrassate
C
4

Of the ones you list, only Hoard will return memory to the system... but if it can actually do that will depend a lot on your program's allocation behaviour.

Coth answered 7/2, 2010 at 0:27 Comment(4)
Thanks! Can you name other allocators, which can return memory back to the systemIncrassate
Actually, it seems like glibc will as well, but the default threshold is for only allocations 128kB and larger to be made in this way. OpenBSD is mmap-backed for all allocations, and so free will almost always return memory. However, there is a big performance tradeoff; mmap-backed memory is much, much slower in many cases, and will induce a lot of page-faults to zero it, which may be even worse than the small amount of swap pressure it saves.Coth
yep, but the OpenBSD's motivation is security, not performance (as my answer mentions). Didn't know about glibc, will investigate, tx.Supposal
As I think now, ptmalloc and most ptmalloc-based and dlmalloc will return memory to the system both via munmap and sbrk(-xxxx).Incrassate
J
3

For all 'normal' mallocs, including the ones you've mentioned, memory is released to be reused by your process, but not back to the whole system. Releasing back to the whole system happens only when you process is finally terminated.

Jo answered 7/2, 2010 at 0:2 Comment(1)
As some people below have mentioned, there are special circumstances when malloc implementations will attempt to return memory to the OS. I wouldn't generally rely on this. Instead think about mmap()ing a new segment for your overnight processing and then unmap when you are done. Obviously you will need to do something about heap management, but allocation can be a very simple pool-style allocation (ie. free is a no-op) since you will release the entire memory segment at the end of your overnight processing job.Jo
J
3

The short answer: To force malloc subsystem to return memory to OS, use malloc_trim(). Otherwise, behavior of returning memory is implementation dependent.

Jackknife answered 23/5, 2013 at 18:25 Comment(1)
malloc_trim() is only available with Linux/glibc.Necessary
S
1

FreeBSD 12's malloc(3) uses jemalloc 5.1, which returns freed memory ("dirty pages") to the OS using madvise(...MADV_FREE).

Freed memory is only returned after a time delay controlled by opt.dirty_decay_ms and opt.muzzy_decay_ms; see the manual page and this issue on implementing decay-based unused dirty page purging for more details.

Earlier versions of FreeBSD shipped with older versions of jemalloc, which also returns freed memory, but uses a different algorithm to decide what to purge and when.

Shelburne answered 18/7, 2019 at 16:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.