A call stack is a runtime Stack used by a Programming Language to keep track of active Functions.

Each time a function is called, the runtime pushes a stack frame onto the call stack. A stack frame usually stores the function’s arguments, local variables, return address, and bookkeeping needed to resume the caller. When the function returns, its frame is popped from the stack.

The call stack follows last in, first out order: the most recently called function must finish before its caller can continue.

flowchart TB
    subgraph Calls["Call and return order"]
        direction LR
        MainCall["main()"]
        Func1Call["func1()"]
        Func2Call["func2()"]
        MainCall -->|"calls"| Func1Call
        Func1Call -->|"calls"| Func2Call
        Func2Call -->|"returns to"| Func1Call
        Func1Call -->|"returns to"| MainCall
    end

    subgraph Stack["Call stack while func2() is running"]
        direction TB
        F2["top: func2()<br/>locals + return to func1()"]
        F1["func1()<br/>locals + return to main()"]
        F0["main()<br/>locals"]
    end

In a nested call such as main() func1() func2(), func2() is at the top of the call stack. It returns to func1(), then func1() returns to main().

If calls keep being added without returning, such as with Recursion that has no reachable base case, the call stack can run out of space. This is called a stack overflow.

Differences between languages

Most major languages have the same basic call stack model: a function call adds a frame, and returning removes it. The differences are usually about who manages the stack, how errors are reported, and whether the language can avoid growing the stack in some cases.

LanguageTypical behaviour
C Language / C++The native machine stack is used directly. Stack frames are low-level and efficient, but stack overflows may crash the program or cause undefined behaviour rather than producing a tidy language error.
JavaEach thread has its own call stack. Deep or infinite recursion usually raises StackOverflowError. Stack traces are a standard debugging tool.
PythonUses interpreter-managed frames on top of the native stack. Python has a recursion limit and usually raises RecursionError before the process stack is exhausted.
JavascriptUses the engine’s call stack. Deep recursion usually raises an error such as RangeError: Maximum call stack size exceeded. Asynchronous callbacks, promises, and async/await do not keep one long synchronous call stack while waiting.
GolangGoroutines start with small stacks that can grow and shrink. This makes many lightweight concurrent call stacks practical.
RustUses the native stack like C and C++, but memory safety rules prevent many stack-related bugs around invalid references. Stack overflow handling depends on platform and build/runtime settings.
Erlang / ElixirEach lightweight process has its own stack and heap managed by the BEAM virtual machine. Recursion is common, and tail-recursive functions can run without growing the stack.
HaskellLazy evaluation means the runtime stack may represent pending computations rather than simple eager function calls. Space leaks can happen when too many unevaluated thunks build up.

Some languages and runtimes support tail call optimisation. With tail calls, a function call in final position can reuse the current stack frame instead of pushing a new one. This is important in functional programming because it allows some recursive code to run like a loop.

Related