It's not a performance optimisation not to give space back. GCs could easily give space back after a GC if they know a range (bigger than a page) is empty, it's just that they rarely know it is empty unless they GC everything, and even then there is likely to be a few bytes used. Hence the various experiments with generational GC, to try to deal with fragmentation.
Many C/C++ allocators don't release to the OS often or ever.
Many C/C++ allocators don't release to the OS often or ever.