6

I saw this answer to a stack overflow question that says that freeing memory at the very end of a c program is actually harmful because it moves variables that wouldn't be used again into system memory.

I'm confused why the free() method in C would do anything different than the operating system reclaiming the heap at the end of the program.

Does anyone know if there is a real difference between free() and termination in terms of memory management and if so how the operating system may treat these two differently?

e.g.

would anything different happen between these two short programs?

void main() {
    int* mem = malloc(1);
    return 0;
}
void main() {
    int* mem = malloc(1);
    free(mem);
    return 0;
}
ngood97
  • 513
  • 5
  • 16
  • 3
    When the app is terminated, the OS releases all resources (including allocated memory). `free()` is relevant when your code is going to continue to execute, and as a good programming habit you should always use it, but when your app closes the OS takes care of final cleanup. – Ken White Apr 21 '19 at 02:07
  • Like that answer you link to - which is an opinion that is valid in some circumstances, and not in others - the answers to this question will also be opinion-based. Voting to close accordingly. – Peter Apr 21 '19 at 02:41
  • It usually reclaims memory in a different way. Why would you ask though? – n. m. could be an AI Apr 21 '19 at 03:08
  • @n.m. I was just curious! I realized I didn't know what the answer would be and I thought it was interesting – ngood97 Apr 21 '19 at 03:27
  • @n.m. As far as I understand on Linux either Slab or buddy Allocator is used to allocate in kernel structures and physical pages which one depends on the memory used (please correct me if I'm wrong). I dont really know the internals of both of them. – Some Name Apr 21 '19 at 11:56
  • @SomeName this has nothing to do whatsoever with `free()` and exiting a program. – n. m. could be an AI Apr 21 '19 at 14:57
  • @n.m. So who is responsible for clean up on exit? – Some Name Apr 21 '19 at 15:12
  • Clean up what exactly? User space allocations do not get cleaned up upon exit. They simply cease to exist together with the entire address space. Slabs and their buddies know nothing about malloc. Linux is not the only OS out there anyway. How are Linux implementation details relevant to the question? – n. m. could be an AI Apr 21 '19 at 15:23

4 Answers4

8

No, terminating a program, as with exit or abort, does not reclaim memory in the same way as free. Using free causes some activity that ultimately has no effect when the operating system discards the data maintained by malloc and free.

exit has some complications, as it does not immediately terminate the program. For now, let’s just consider the effect of immediately terminating the program and consider the complications later.

In a general-purpose multi-user operating system, when a process is terminated, the operating system releases the memory it was using to make it available for other purposes.1 In large part, this simply means the operating system does some accounting operations.

In contrast, when you call free, software inside the program runs, and it has to look up the size of the memory you are freeing and then insert information about that memory into the pool of memory it is maintaining. There could be thousands or tens of thousands (or more) of such allocations. A program that frees all its data may have to execute many thousands of calls to free. Yet, in the end, when the program exits, all of the changes produced by free will vanish, as the operating system will discard all the data about that pool of memory—all of the data is in memory pages the operating system does not preserve.

So, in this regard, the answer you link to is correct, calling free is a waste. And, as it points out, the necessity of going through all the data structures in the program to fetch the pointers in them so the memory they point to can be freed causes all those data structures to be read into memory if they had been swapped out to disk. For large programs, it can take a considerable amount of time and other resources.

On the other hand, it is not clear it is easy to avoid many calls to free. This is because releasing memory is not the only thing a terminating program has to clean up. A program may want to write final data to files or send final messages to network connections. Furthermore, a program may not have established all of this context directly. Most large programs rely on layers of software, and each software package may have set up its own context, and often no way is provided to tell other software “I want to exit now. Finish the valuable context, but skip all the freeing of memory.” So all the desired clean-up tasks may be interwined with the free-memory tasks, and there may be no good way to untangle them.

Software should generally be written so that nothing terrible happens if a program is suddenly aborted (since this can happen from a loss of power, not just deliberate user action). But even though a program might be able to tolerate an abort, there can still be value in a graceful exit.

Getting back to exit, calling the C exit routine does not exit the program immediately. Exit handlers (registered with atexit) are called, stream buffers are flushed, and streams are closed. Any software libraries you called may have set up their own exit handlers so that they can finish up when the program is exiting. So, if you want to be sure libraries you have used in your program are not calling free when you end the program, you have to call abort, not exit. But it is generally preferred to end a program gracefully, not by aborting. Calling abort will not call exit handlers, flush streams, close streams, or perform other wind-down code that exit does—data can be lost when a program calls abort.

Footnote

1 Releasing memory does not mean it is immediately available for other purposes. The specific result of this depends on each page of memory. For example:

  • If the memory is shared with other processes, it is still needed for them, so releasing it from use by this process only decrements the number of processes using the memory. It is not immediately available for any other use.
  • If the memory is not in use by any other processes but contains data mapped from a file on disk, the operating system might mark it as available when needed but leave it alone for the moment. This is because you might run the same program again, and it would be nice if the data were still in memory, so why not just leave it in place just in case? The data might even be used by a different program that uses the same file. (For example, many programs might use the same shared library.)
  • If the memory is not in use by any other processes and was just used by the program as a work area, not mapped from a file, then system may mark it as immediately available and not containing anything useful.
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Excellent answer. You might add that `return x;` from `main()` is equivalent to calling `exit(x);`, including the implicit `return 0;` at the end of `main()`. – chqrlie Apr 21 '19 at 23:44
  • Also worth adding is `abort()` **does** release allocated memory back to the OS, but might not flush pending output nor remove temporary files. – chqrlie Apr 21 '19 at 23:51
4

would anything different happen between these two short programs?

The simple answer is: it makes no difference, the memory is released to the system in both cases. Calling free() is not strictly necessary and does incur an infinitesimal overhead but may prove useful when trying to track memory leaks in more complex programs.

Does terminating a program reclaim memory in the same way as free?

Not exactly:

  • Terminating a program releases the memory used by the program, be it for the program code, data, stack or heap. It also releases some other resources such as file handles, device handles, network sockets... All this is done efficiently, no matter how many blocks of memory have been allocated with malloc().
  • Conversely, free() makes the block of memory available for further use by the program for later calls to malloc() or realloc(). Depending on its size and the implementation of the heap, this freed block may or may not be returned to the OS for use by other programs. Also worth noting it the fragmentation problem, where small blocks of freed memory may not be usable for a larger allocation because they are surrounded by allocated blocks. The C heap does not perform packing or de-fragmentation, it merely coalesces adjacent free blocks. Freeing all allocated blocks before leaving the program may be useful for debugging purposes, but may be complicated and time consuming, while not necessary for the memory to be reused by the system after the program terminates.
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Is that a standardized behavior? If so could you provide some reference? – Some Name Apr 22 '19 at 08:41
  • Yes, this is standard behavior for user programs in all modern operating systems. See this answer: https://unix.stackexchange.com/a/275280/205584 . Some embedded systems might not follow this behavior, as well as very thin runtime systems, where the effect of leaving the `main()` function varies from one system to another. – chqrlie Apr 22 '19 at 14:02
2

free() is a user level memory management function and depends on malloc implementation you are currently using. The user-level allocator might maintain a linked-list of memory chunk and malloc/free will take the chunk of appropropriate size/put it back.

exit() Destroys an address space and all regions. This is related to malloced heap as well as some other regions and in-kernel data structures used for managing address space of the process:

Each address space consists of a number of page-aligned regions of memory that are in use. They never overlap and represent a set of addresses which contain pages that are related to each other in terms of protection and purpose. These regions are represented by a struct vm_area_struct and are roughly analogous to the vm_map_entry struct in BSD. For clarity, a region may represent the process heap for use with malloc(), a memory mapped file such as a shared library or a block of anonymous memory allocated with mmap(). The pages for this region may still have to be allocated, be active and resident or have been paged out

Reference: https://www.kernel.org/doc/gorman/html/understand/understand007.html

Some Name
  • 8,555
  • 5
  • 27
  • 77
  • You make it sound like `exit()` does something weird that would not otherwise happen. Returning from `main()`, including without a `return` statement does call `exit()` too. – chqrlie Apr 21 '19 at 23:45
  • @chqrlie If the `SYS_exit` syscall is not called explicitly how would the program exit. I do not mean that we always have to call `exit()` std library function. I meant the `SYS_exit` should be in the end of the program. Anyway in the absent of explicit `SYS_exit` the program most likely segfaults, no? I – Some Name Apr 22 '19 at 05:26
  • 2
    Actually I am saying that returning from `main()` with `return exit_code;` is equivalent to calling `exit(exit_code)`. The OP does not call `exit()` explicitly but finishes the program with `return 0;` which is equivalent to calling `exit(0)`. Without this precision, it is not obvious that your explanation answers the question. Furthermore `exit()` is one of the C library functions that terminates the process, other ones such as `abort` also do that and terminating the process is what allows the OS to reclaim the memory. – chqrlie Apr 22 '19 at 07:41
  • @chqrlie Judging by what's documented in the 7.22.4 about `abort()` it is abnormal termination and with raising the corresponding signal. The link I added explicitly specify actions on `exit()`. Also it is noted that _During process exit, it is necessary to unmap all VMAs associated with a `mm_struct`._ I suppose that it means normal as well as abnormal termination. – Some Name Apr 22 '19 at 08:19
  • 2
    all these considerations are specific to linux, the OP's question is much more general and the answer should be simple: *it makes no difference, the memory is released to the system in both cases. Calling `free()` is not strictly necessary and does incur an infinitesimal overhead but may prove useful when trying to track memory leaks in more complex programs*. – chqrlie Apr 22 '19 at 08:26
2

The reason well-designed programs free memory at exit is to check for memory leaks. If your application-level memory allocation does not go to zero after your last deallocation, you know that you have a memory memory that is not being managed properly and probably have a memory leak in your code.

would anything different happen between these two short programs?

YES

I'm confused why the free() method in C would do anything different than the operating system reclaiming the heap at the end of the program.

The operating system allocates memory in pages. Heap managers (such as malloc/free implementations) allocate pages from the operating system and subdivide the pages into smaller allocations. Calls to free() normally return memory to the heap. They do not return the pages to the operating system.

user3344003
  • 20,574
  • 3
  • 26
  • 62
  • This answer is confusing: while you give a good reason for freeing allocated memory before returning to the system, this is not necessary unless you are running programs on a very poorly designed operating system, or no operating system at all, so the answer if not YES, it should be **NO** it does not make a difference on regular operating systems, except for a very small overhead, which might become noticeable if a lot of calls to `free()` are required to free all memory blocks. – chqrlie Apr 21 '19 at 23:48