MemoryBeginner

Stack vs heap

“Where do local variables live and where do objects live? Why does the split exist, and what does each choice cost?”

What this tests

  • Stack as per-thread, LIFO, lifetime tied to the frame
  • Heap as process-wide, arbitrary lifetime, managed by an allocator
  • Why the split exists: known vs unknown lifetime
  • Costs: allocation speed, fragmentation, overflow, RSS behaviour after free

Answers by level

Read the beginner answer first and notice what is missing.

The stack is a per-thread, contiguous region of the address space that grows and shrinks with function calls. A call pushes a frame — return address, saved registers, locals; a return pops it. Allocation is "move the stack pointer", freeing is implicit, and a local cannot outlive its frame: its lifetime is exactly the call. The size is fixed at thread creation — 8 MB of virtual space for the main thread on Linux by default (ulimit -s), 1 MB on Windows, whatever pthread_attr_setstacksize says for other threads — with a guard page beneath so that running past the end faults instead of corrupting memory (Stack Frames, Stack Overflow).

The heap is the process-wide pool for data whose lifetime is not tied to any call: it is created in one function and used long after that function returned. An allocator (malloc/free, new/delete, or a garbage collector in managed languages) carves it out of large regions obtained from the kernel with brk/sbrk and mmap. Because objects are freed in arbitrary order, the allocator must manage free lists, size classes and fragmentation, and a lookup is tens of nanoseconds rather than one instruction (What Happens When I Allocate Memory?).

So the split exists because of lifetime knowledge: when the compiler knows an object dies with the call, the stack is free; when it does not, something has to track it. In C++ you choose per object; in JavaScript and Python every object lives on the heap and locals are references to them, though JITs (V8, PyPy) use escape analysis to keep short-lived objects out of the heap. Recursion has a limit because every frame consumes stack, and stack size is fixed.

To the kernel there is no "stack" or "heap" — they are mappings in the address space (The Process Memory Layout). Which is why the failure modes differ: a stack overflow is a SIGSEGV at the guard page (or RangeError/RecursionError in runtimes that count); a heap problem is a leak, a use-after-free, or an RSS that never shrinks because free gave memory back to the allocator, not to the OS.

Green flags · Red flags

Strong green flag · Relates allocator behaviour (arenas, mmap threshold) to RSS seen in production.
Green flags
  • Explains lifetime as the reason for the split
  • Describes the stack as per-thread with a fixed size and a guard page
  • Knows the heap is managed by an allocator with fragmentation and free lists
  • Says the kernel only sees mappings
  • Mentions that free does not necessarily shrink RSS
Red flags
  • "Stack is faster" with no reason
  • Believes the heap is a fixed region set by the OS
  • Thinks stack overflow corrupts the heap

Follow-up questions

F1
Why does returning a pointer to a local variable break?
F2
Why did RSS not drop after freeing a 2 GB structure of small objects?
F3
Where does a JavaScript local variable holding an object live?

Scenario

A C++ service crashes with SIGSEGV only in its worker threads when parsing deeply nested input; the same input is fine in a unit test. Explain the memory mechanism and two fixes.

Learn this topic