My test code shows that after free()
and before the program exits, the heap memory is returned to the OS. I use htop
(same for top
) to observe the behaviour. My glibc version is ldd (Ubuntu GLIBC 2.31-0ubuntu9.9) 2.31
.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUFSIZE 10737418240
int main(){
printf("startn");
u_int32_t* p = (u_int32_t*)malloc(BUFSIZE);
if (p == NULL){
printf("alloc 10GB failedn");
exit(1);
}
memset(p, 0, BUFSIZ);
for(size_t i = 0; i < (BUFSIZE / 4); i++){
p[i] = 10;
}
printf("before freen");
free(p);
sleep(1000);
printf("exitn");
}
Why this question Why does the free() function not return memory to the operating system? observes an opposite behaviour compared to mine? The OP also uses linux and the question is asked in 2018. Do I miss something?
2
Answers
I did some experiments, read a chapter of The Linux Programming Interface and get an satisfying answer for myself.
First , the conclusion I have is:
malloc
uses system callsbrk
andmmap
under the hood when allocating memory.brk
ormmap
allocating mem depending on how much you request.brk
, the process is probably not returning the memory to the OS before it terminates (sometimes it does). If bymmap
, for my simple test the process returns the mem to OS before it terminates.Experiment code (examine memory stats in
htop
at the same time):code sample 1
When it comes to
"free donen"
, andsleep()
, you can see that the program still takes up the memory and doesn't return to the OS. Andstrace ./a.out
showingbrk
gets called many times.Note:
I am looping
malloc
to allocate memory. I expected it to take up only 1GiB ram but in fact it takes up 8GiB ram in total.malloc
adds some extra bytes for bookeeping or whatever else. One should never allocate 1GiB in this way, in a loop like this.code sample 2:
This one is similar to sample 1, but it allocate again after
free
. The scecond allocation doesn't increase memory usage, it uses the freed yet not returned mem again.code sample 3:
This one takes from The Linux Programming Interface and I simplifiy a bit.
Chapter 7:
Try run with:
./free_and_sbrk 1000 10240 2
./free_and_sbrk 1000 10240 1 1 999
./free_and_sbrk 1000 10240 1 500 1000
you will see only for the last example, the program break decreases, aka, the process returns some blocks of mem to OS (if I understand correctly).
This sample code is evidence of
"If allocating by
brk
, the process is probably not returning the memory to the OS before it terminates (sometimes it does)."At last, quotes some useful paragraph from the book. I suggest reading Chapter 7 (section 7.1) of TLPI, very helpful.
What is program break (also from the book):
Also: https://www.wikiwand.com/en/Data_segment
Linux treats allocations larger than
MMAP_THRESHOLD
differently. See Why does malloc rely on mmap starting from a certain threshold?The question you linked, where allocations may not appear to be fully reclaimed immediately, uses small allocations which are sort of pooled together by
malloc()
and not instantly returned to the OS on each small deallocation (that would be slow). Your single huge allocation definitely goes via themmap()
path, and so is a totally independent allocation which will be fully and immediately reclaimed.Think of it this way: if you ask someone to buy you eggs and milk, they will likely make a single trip and return with what you requested. But if you ask for eggs and a diamond ring, they will treat those as two totally separate requests, fulfilled using very different strategies. If you then say you no longer need the eggs and the ring, they may keep the eggs for when they get hungry, but they’ll probably try to get their money back for the ring right away.