It's also a way, I suppose, to ensure your program has the memory it needs, depending on how you design it. Mine basically allocates all the expected memory I need at the start, while still being able to grab more if it needs. This was more of an issue back when you shared a server with lots of others.
(In fact, most libcs let you just redefine the malloc(), free(), and realloc() functions to point to your own allocator instead of the default one, so you don't have to rewrite all the functions calling them. E.g., the mimalloc allocator [0] can be configured as a drop-in replacement for the libc allocator.)
Re-reading your comment, about the "not really freeing anything" -- I beg to differ, as when you do a real free(), it frees up memory for any program to use. As I already mentioned one of the benefits is once you have control of who can use that memory and aren't risking it not being available - disregarding some program that constantly grows in memory.
This is an extremely naive description of not only what the libc free() function does, but also of how memory management works in general.
The free subroutine deallocates a block of memory previously allocated by the malloc subsystem. Undefined results occur if the Pointer parameter is not an address that has previously been allocated by the malloc subsystem, or if the Pointer parameter has already been deallocated. If the Pointer parameter is NULL, no action occurs.
All that is guaranteed, is a deallocation. Not how and when that memory will become available again.
Your "more detailed" understanding will break, and will cause headaches, on platforms ypu're not used to.
overcommit would like to have a word