To free or not to free in C
von Felix Mößbauer
In the programming language C the programmer is responsible for handling the (heap) memory allocations.
This means, whenever memory is allocated on the heap using e.g.
malloc, the programmer has to ensure that this allocation is released to the OS again.
This can be done using
free. So obviously the answer to the question in the title is that each
malloc has to be followed by a
free at some time. However there are some situations, where this answer is just too easy.
OS Memory Handling
For a more detailed answer we have to check how allocations are handled by the OS. As this would exceed this blog-post, we refer to the related Wikipedia article.
When is the memory returned to the OS?
Many people guess whenever a
free is called, the released memory is almost instantly returned to the OS. However this does not hold in most situations. In most cases the memory is just released to the program, so that the next
malloc (same program of course) first tries to use this memory (stored internally in a free-list). If the available memory is not large enough - which could also be due to fragmentation - fresh memory from the OS is requested. This is also stated in the GNU documentation of malloc
There is one exception for this rule of thumb: If the requested (and freed) memory is larger than a memory page. Then, the whole page is returned to the OS. As this is also not specified, some implementations might perform different. However most C-libs on at least linux-based OS do it this way.
A common problem related to allocations in C is the exception handling: As there is no RAII support (like in C++, see next section), the programmer has to track allocations in each part of the program. This is often tricky and leads to many lines of code for deallocation spread over many functions. To avoid double-frees - which generate a segmentation fault - it is best practice to null the pointer after freeing:
int* p = malloc(10 * sizeof(int)); //... free(p); p = null;
Freeing a null-pointer is specified and valid behavior.
Terminating the Application
This is almost the only situation, where memory must not be freed. In fact it is even recommended not to free the allocated memory directly before terminating the application, as the OS will do that for you. This speeds up the termination, as each to-be-freed memory location has to be transferred into the memory (e.g. from swap space) before actually freeing it. This can be avoided by not freeing the memory but relying on the "garbage collection" of the OS.
However under any circumstances, this strategy has to be chosen by design and not as a result of a leaky memory management.
What about valgrind
If following this strategy, memory profiling / tracking applications (e.g. valgrind) will always report memory-leaks. These should not be ignored, as they can hardly be distinguished from actual leaks. Hence, we recommend to use preprocessor macros to free the memory if using a tracker. This also ensures, that the application has no leaks except the missing frees at the end.
// ... #ifdef VALGRIND free(p); #endif
If possible prefer C++ over C as this takes care of the memory handling in most situations. There, try to implement your application using STL containers instead of manually allocating memory regions. In short: Avoid
The magic behind that is called RAII (Resource Acquisition Is Initialization). Whenever an object is constructed, the necessary resources are allocated and initialized. When the object later leaves the scope (even due to a exception), the resources are released again. This totally avoids the problems of tracking memory allocations (as described in section Exceptions).
And the best: If coding correctly, C++ code is as fast (or even faster - due to better knowledge of the types in the optimization step) as your C code.
However due to RAII it is tricky to tell C++ not to free the memory at the end. If really necessary, you could use your own allocator and set a flag (directly before terminating the program) not to free the memory on destruction of the allocator. But at least we know only very rare cases where this technique is used.
Best Practice Guide
A hint is to avoid small and short-lived allocations. If using C++ STL containers like
std::vector this is not a big deal, as the allocator of the container takes care of efficient memory management. Otherwise use allocators (yes, that is also possible in C). Going into detail would exceed this topic horribly.
For tiny and short-lived allocations it is also possible to use
alloca. Memory requested this way is allocated on the stack and automatically freed after leaving the function (not the scope).
And last but not least: Don't free memory at the end of the program, but do so by design and not because you lost the information. An easy way to check that is by following the approach described in "What about valgrind".
von Felix Mößbauer
Reverse Engineering a Dotnet Monitor
von Felix Mößbauer
Einen Kommentar schreiben