Please tell us next time the language you use and try to give a compilable example of the program which causes the error. Seem to be a macroed C though. And use Code-Tags, please
Your comment is wrong and will lead into the confusion which is visible in your following code. What you did here is not allocating a variable on the heap, but allocating a pointer to an array (which is again a pointer to its first element) on the stack:
TMemFile*: The type -> Pointer to TMemFile
mptr[20000]: array of this type named mptr -> Pointer to an array of 20000 TMemFiles.
When you want to have an Array of mptr on the heap, use
TMemFile *mptr;
So you will get a pointer to TMemFile on the stack and can allocate memory on the heap for its elements. If you want to allocate 20000 elements of TMemFile on the heap now, just
mptr = malloc(20000 * (sizeof(TMemFile)));
and address the elements through offsets.
PS: Your cleanup is wrong. Free the elements in the for-loop, then (outside the loop) free the array itself.
Your assumption that VM usage will go down when free() is called isn’t valid. In Unix/Linux processes can only increase the heap, not shrink it. If a heap page isn’t needed it is mapped out of the working set, that’s all.
Perhaps top was measuring the pages in use (and those measurements need to be taken with a grain of salt, because it may not be measuring what you believe it is) and there are no references active to those pages, whereas in the second page, it’s still in the malloc allocation pool (maintained by the malloc library, not the kernel).
In any case you have little control over how VM is allocated in a Linux process. There is a brk() syscall to extend the data segment, but there is not a corresponding syscall to contract it. That’s the way it is.