Should I free up all allocated memory at the end of a program?

22

It is commonly accepted that when I allocate a block of memory I am responsible for releasing it. This is particularly true when programming based on RAII. However the following program works perfectly:

int main() {
    int* ptr = new int[99999];
    return 0;
}

Here the system should be able to release everything the process has allocated.

What is considered good practice here? Should I always release the memory that I allocate? Is there a problem / advantage in leaving this work to the system?

    
asked by anonymous 16.02.2014 / 15:49

3 answers

18

One point to consider is that code that is now a complete program can eventually be refactored to become a feature of another larger program. If the original program did not care about releasing the resources (why the OS was going to do this for it), it will now start to generate a leak in the larger program each time its functionality runs. Many debugging hours will be lost until you identify the source of the problem, and even more so by refactoring the original code to behave properly.

    
17.02.2014 / 11:51
7

I do not consider it a good practice to let the system release all the resources that have been allocated by the program. I must remember that memory is only one of the resources that the system must manage and if, the only feature that your program uses is only memory, which I find unlikely in medium or large programs, nevertheless, I do not consider it a good practice.

The exit function, according to the author's own answer, should be used with caution and its side effects must be known. One of the problems of using it is:

  • Creating multiple exit points of a program, which can make debugging difficult;
  • It can make it difficult to read a program, basically, it means multiples goto for the end of the program;
  • Does not release features clearly and cleanly.

Actually, I point out that calling the exit function is different than calling the return statement at the end of the main program. While the return statement does "unwind stack", which allows calling all destructors of local objects, the exit function does not, which can cause errors of logic, not easily detectable in a debug.

I also note that the idea of letting the SO take care of releasing the resources only happens in "modern" OS. In SOs, such as those of the FreeRTOS type, it is the total responsibility of the programmer to release the allocated resources.

Again, I come back to the question that memory is only one of the managed resources. Modern OSs release all the resources allocated by the program, at the end of the process, no matter what state they are in (if you have to flush a file, to save the data, this will not happen at the end of the process). The author, in his reply, suggests the creation of callbacks or a messaging system to aid the process. I think this only complicates the logic of the system. I think it's a very low price to let the system do the automatic destruction of all objects in a clear, clean and safe way, even if it causes the memory to swap out of the swap. And visualizing the current hardware systems, where memory is a resource that can be considered plentiful, does not justify all that logic anymore.

    
17.02.2014 / 01:28
7

In any modern operating system the virtual memory pages feature is used. In this model each process owns an entire address space and the system has the responsibility of mapping the address that the process wants to read to the address where that memory actually is. This gives the system several useful optimization opportunities, such as putting unused pages on the disk (swap), rescuing them when they are required, or applying copy-on-write (CoW) where two processes share the same page. >

In this way the system already knows exactly which memory belongs to each process. And releasing that memory is as simple as marking it as a garbage page that can be reused in another process. That is, for any modern computer, ending a process means releasing all of its memory.

Considering a large program, there are several problems when you press "close". The destructor of all objects will be executed in sequence, causing a lot of half-memory to be accessed and read from the disk (if any swap occurred) and causing visible slowness to the end user. The HD life is spent while wasting user time, all wrong. This just to release features that will already be released by the system anyway, and more efficiently (it frees pages instead of releasing object by object, and does not need to read data from disk).

Whether this is good practice is questionable. The object-orientation design hopes that everything that is built will, at some point, be destroyed. Then there may be some important logic going on in the destructor, like saving settings to disk. In this case merely calling exit(0) can be harmful. Another way is to have a Boolean global variable that is true when the process is about to close and have each destructor check this variable, bypassing unnecessary memory releases. But here there is little advantage because a lot of memory will need to be read anyway (we go back to the swap) and the logic of destruction gets complicated and it's not trivial.

One solution may be to have a notification engine that sends some kind of signal to all objects that need to do something else at the end of the program. It can be a list of callbacks for example. This is probably the most efficient way that does not completely break the object-oriented structure.

The big problem is that tools that detect leak will accuse a lot of problems in your program. Hence it is interesting to have two termination paths, one deallocating everything (for debugging) and another going straight to exit(0) . The two must be equally tested, of course. But the second does not have to go through the leak detector.

But remember that there are other features than memory. The interprocessor communication mechanisms of System V (available in some * nix, including linux), for example, are permanent and survive the termination of the by-design process. It is essential for them to be released.

All this discussion is only valid for languages where you programmer does memory management. For garbage collector languages there is usually not much choice.

    
16.02.2014 / 15:49