Search code examples
cstack-memory

C function local variable does not get freed after function execution


In the following C program, a call to a function creates a rather large local variable buf, then the function returns and the programs stays in an infinite loop. Upon the function getting called, the system's RAM usage rises up by an amount equal to the size of buf, as one could expect; however, after the function returns ("Done" is displayed), the RAM usage does not go down and instead remains constant.

Why does this stack-allocated buffer not get freed right-away after the function returns? How can one formulate the general behaviour for this, and describe in which situations it may or may not get freed right away?

#include <stdlib.h>
#include <stdio.h>

void f()
{
    int buf[1000 * 1000 * 200];
}

int main()
{
    f();
    printf("Done\n");
    for (;;);
    return 0;
}

Below is the RAM usage timeline when running the program. RAM usage timeline

P.S. This was run on Linux Ubuntu.

P.S. I compiled with no compiler optimization

P.S. Calling f in a "blinking" manner (repeated [on - delay - off - delay]) instead of calling it one shot leads to a similar RAM usage timeline.


Solution

  • Why does this stack-allocated buffer not get freed right-away after the function returns?

    In general, stack-allocated memory is never freed after the function returns, at least, not in the way you seem to expect.

    As the stack grows, more memory is automatically allocated to it by the operating system. But it's a one-way process — as functions return and the stack is popped, memory is not deallocated from the process. The assumption is that if the program ever needed that many stack frames (or that big of a stack frame) at one time, it is likely to need it again. And of course this is a decently valid assumption: any function that got called and that caused the stack to grow to a certain depth, might get called again.

    On the other hand, repeatedly deallocating stack memory as each function returned and reallocating it as functions were newly called, would in most cases be totally inefficient (not to mention unnecessary).

    To say a little more...

    In general, there are two or three different levels of memory allocation:

    1. Memory can be allocated to the process (that is, by the OS), or not.
    2. Memory can be in use by the process, or not.
    3. On a system with virtual memory, memory can be in the process's active working set (or "resident set"), or it can be swapped out.

    When you call a function and the stack grows, it makes use of allocated but unused stack space if it's there, or the OS automatically allocates more memory (in sense 1) if there's not enough. But when a function returns, its former stack space merely becomes unused (in sense 2); it is not deallocated from the process and returned to the OS (in sense 1).

    Similarly, when you call malloc to ask for some dynamically-allocated memory, the malloc implementation makes use of its "pool" (or heap") of available memory if there's enough of it, or it asks the OS for more memory (in sense 1) if there's not enough. But when you call free to return dynamicaly-allocated memory you no longer need, it goes back into malloc's pool and becomes unused (in sense 2); it is generally not deallocated from the process and returned to the OS (in sense 1).

    The swapping in or out of virtual memory (sense 3) is totally automatic, although in general it's pretty well correlated with whether the memory is in use or not (in sense 2).