On many operating systems, free() doesn't make the memory available for the OS again, but "only" for new calls to malloc(). This is why you don't see the memory usage go down externally, but when you increase the number of new allocations by threading, the memory is re-used so total usage doesn't go through the roof.