Skip to the content
Software Made Clear Diagrams that show the mechanism About

Stack vs heap

Compare call-stack frames and independent heap allocations, with the ownership, lifetime and runtime details that shape the choice.

5 min read Updated September 23, 2026 Last verified September 23, 2026
ANSWER

A call stack reclaims frames in last-in, first-out order when calls return. Heap allocations can be released independently of a call frame. Lifetime does not dictate placement: a scoped object can live on the heap, and some automatic storage sizes are chosen at runtime.

IN PLAIN TERMS

A stack of plates is used from the top: finish with the top plate and its space is available again. A cupboard lets you take out one item while others remain. The cupboard needs its own way to track free space and who is responsible for putting items back.

A value read back wrong after the function that owned it returned. A crash that says “stack overflow” with no obvious recursion anywhere. Both are the same misunderstanding about where a thing lives and how long it is allowed to live there. Both trace to the same fork in how a program is allowed to ask for memory — and once you see the fork, both stop being mysterious.

STACK — one end, fixed orderHEAP — anywhere, any orderpush / pop happen only herefree listfree listtoo small for the next requestSTACK — one end, fixed orderpush / pop happen only hereHEAP — anywhere, any orderfree listfree listtoo small forthe next request
The stack grows and shrinks at one end, in a fixed order. The heap is filled and emptied anywhere, in any order — and the gaps that leaves behind are not always big enough for what comes next.

Why a call frame can be cheap#

Each thread normally has its own call stack. Process vs thread explains which memory threads share and which execution state each keeps separately. In a common fixed-size-frame implementation, entering a function adjusts the stack pointer to reserve space and returning restores it; that last-in, first-out discipline needs no search for a free block. Frame size is not universally a compile-time constant. C implementations that support variable-length arrays, for example, can reserve automatic storage whose size is determined at runtime. The exact instructions and placement of local values depend on the compiler and runtime, but the stack’s defining constraint is its order of reclamation, not that every lifetime or size was known in advance.

When a function returns normally, its call frame can be reused without a matching free call. That is why forgetting to free the frame is not the usual memory-leak mechanism. Deep recursion and calls that do not return can still keep stack space occupied. An object whose storage is tied to a call cannot safely be used after that call has returned; a separate allocation can have a different lifetime.

What independent allocation buys#

Heap allocation lets an object be released independently of the current call frame. It is useful when data must outlive a call, but a caller can also deliberately heap-allocate an object with a short lexical lifetime. The allocator tracks available space and finds a region for each request; its cost depends on the allocator strategy. Lifetime can guide the choice, but it does not mechanically determine where an object lives.

That cost is not one number. A bump allocator that never frees individual objects is almost as cheap as a stack push. A size-class allocator keeps separate free lists per size band, so a common-sized request is close to a lookup, not a scan. What does real work is a general-purpose allocator juggling many live, differently sized objects at once — walking or indexing a free list to find a block that fits, sometimes splitting a larger block to make one, sometimes discovering that nothing fits even though the total free space would be enough, because it is scattered into pieces too small individually. That scattering is fragmentation, and it is a cost the stack structurally cannot incur, because nothing on the stack is ever freed out of order. The layout you hand the allocator is also a choice, not a given — a deque’s fixed-size blocks are a concrete case of choosing a memory layout for the access pattern instead of one contiguous array, so the structure asks the allocator for many small, uniform pieces instead of one that grows and gets copied.

Heap storage also needs a release policy. In C, failing to free an allocation after it is no longer needed leaks it. A garbage collector can reclaim unreachable managed objects, but retained references can still keep unwanted objects alive. Neither manual nor automatic reclamation means every heap object’s lifetime must exceed a function call; storage duration and allocation region are separate choices.

Picking one on purpose#

In C, returning a pointer to a local automatic object gives the caller a pointer whose referent is no longer alive after the function returns. A compiler may warn, but code that ignores the warning can appear to work in a short test. Access through that pointer has undefined behavior; another call may reuse the storage, and an optimizer may make different assumptions altogether. A plausible-looking value is not evidence that the object still exists.

This is why the bug is so hard to pin down: it does not depend on the value being wrong in some obvious way, it depends on the exact sequence of calls that happens to run after the dangling pointer is created. Change what gets called next — add a log line, reorder two functions, run the test suite in a different order — and a different frame lands on that memory, so the garbage you read back changes or disappears entirely. A bug that reproduces on Tuesday and not on Wednesday, with no code change in between, is worth checking for exactly this before anything more exotic.

A thread has a stack-space limit that varies by environment. Deep recursion or a large automatic buffer can exceed it. A traversal can replace recursive calls with an explicit, heap-backed work stack; that moves storage pressure to a separately managed allocation, which can also run out of memory.

Situation Take Because
Small call-scoped scratch space in C Consider automatic storage Often stack-backed, if capacity and the implementation allow it.
Value must outlive its creating call Choose independent storage Heap, caller-owned or static storage can outlive that frame.
Large buffer or deep recursion Avoid stack pressure Heap-backed or reusable storage can avoid a frame-size ceiling.
You are not sure who frees it Neither yet Decide the owner before allocating.
Situation
Small call-scoped scratch space in C
Take
Consider automatic storage
Because
Often stack-backed, if capacity and the implementation allow it.
Situation
Value must outlive its creating call
Take
Choose independent storage
Because
Heap, caller-owned or static storage can outlive that frame.
Situation
Large buffer or deep recursion
Take
Avoid stack pressure
Because
Heap-backed or reusable storage can avoid a frame-size ceiling.
Situation
You are not sure who frees it
Take
Neither yet
Because
Decide the owner before allocating.

IF YOU REMEMBER ONE THING

The stack reclaims call frames in order; the heap can release allocations independently. Choose storage for the ownership and lifetime your program needs, then account for the allocator and cleanup policy used by that language and runtime.

Questions people also ask

5 QUESTIONS
Can the stack leak?

A normally returning call reclaims its frame without a matching free call, so forgetting to free that frame is not the usual leak mechanism. Unbounded recursion or a call that never returns can keep stack space occupied; retaining heap objects or other resources is a separate concern.

What actually causes a stack overflow?

The program exceeds the stack space available to the thread, often through deep recursion or a large automatic object. The limit and whether a stack can grow depend on the operating system and runtime.

Is the heap always slower?

Not always. A simple bump allocator can be cheap, while general-purpose allocation may do more bookkeeping. Total cost also depends on access patterns, locality, optimization and reclamation, so measure the workload rather than assuming one universal speed ratio.

Where do globals and static values live?

In a typical native executable, objects with static storage duration occupy a data or zero-initialized segment for the program's lifetime. The exact memory layout depends on the language, linker and runtime.

Does garbage collection remove the difference?

No. Garbage collection determines when unreachable managed objects can be reclaimed; it does not guarantee where each object is allocated. A runtime may use escape analysis to avoid some heap allocations, but even a short-lived, non-escaping object is not necessarily placed on the stack. Placement depends on the language, runtime and optimization.

The library Browse all articles