It does appear that both C and CPython are using munmap behind the scenes (probably because of the malloc+free implementation they are using) to free large blocks of contiguous memory.
Note that this is a glibc feature; other malloc+free implementations may not be this smart. So it may be mostly Linux that does this.
See also this manual page, which unfortunately only hints at the full functionality.
Below is an older article I wrote about traditional malloc+sbrk behavior, which probably also relates to how Linux/glibc malloc+free do smaller memory allocations:
When you request a chunk of memory via malloc(), if there is already enough memory sbrk()'d to satisfy your malloc() request, then some of that memory will be parted out to your program as the return value from malloc().
If not, then more memory will be sbrk()'d to be able to handle the request.
The thing is, with most C libraries, although you can free malloc()'d memory easily with free(), there isn't an easy way to un-sbrk() the pool that's used by malloc.
ISTR that GNU malloc, however, will watch to see if all the malloc()'d chunks near the end of your process's sbrk() pool for malloc have been free()'d, and if they have, then it will sbrk() back down. -But-, ISTR that if you were to malloc() 2 gig, then malloc one byte, then free the 2 gig chunk, I don't think it'll sbrk() back down in that case, because all the chunks at the end have to be unused to sbrk() back down.
But most malloc()'s don't do that. So if you malloc 2 gig, then free
it, then malloc nothing over a megabyte from there on, you will often
still have at least 2 gig sbrk()'d for future use.
You can e-mail the author with questions or comments: