Stack vs Heap Memory: What Every Developer Should Know
Basics4 minInnovialabs editorial team
In short
The stack is fast memory for a function's local data that is freed automatically when the function returns; the heap is flexible memory for data with any lifetime, but allocating there costs more.
The short answer
A program keeps its data in two main memory areas:
- The stack holds local variables and call bookkeeping. Allocation and release are nearly free: entering a function creates a frame, returning removes it in one step.
- The heap holds data that must outlive a function call or whose size is only known at runtime. Memory is allocated explicitly (
new,malloc) or implicitly, and freed manually, by a garbage collector or by ownership rules.
| Stack | Heap | |
|---|---|---|
| Lifetime | While the function runs | As long as the data is needed |
| Allocation speed | Very fast | Slower |
| Size | Limited, set by the OS and settings | Much larger |
| Who frees it | Automatic on return | GC, programmer or owner |
| Access | Each thread has its own | Shared by all threads |
How the stack works
The stack is LIFO — last in, first out. When function a calls b, the frame for b is pushed on top of a. When b returns, its frame is popped and all its local variables are gone.
That is why in C you must never return a pointer to a local variable: after the function returns, that memory belongs to someone else.
How the heap works
The heap is a large pool from which an allocator hands out blocks of the requested size. It is flexible, but it has costs:
- the allocator has to find a free block;
- memory can become fragmented;
- in GC languages every allocation is future work for the collector;
- objects are scattered in memory, which is worse for the CPU cache.
Value types vs reference types
The popular shortcut "value types live on the stack, reference types on the heap" is inaccurate. More precisely: a value type stores the data itself, a reference type stores the address of data on the heap. Where a value ends up depends on where it is declared.
- Java: primitives (
int,double) are values; objects live on the heap and variables hold references. The JIT can avoid heap allocation through escape analysis. - C#:
structis a value type,classis a reference type. But astructstored in a class field lives on the heap together with the object. - Go: the compiler decides placement through escape analysis. If a value "escapes" the function (for example, you return a pointer to it), it goes to the heap.
- Python and JavaScript: almost everything is a heap object, and placement details are hidden by the engine.
The practical consequence is different copy semantics:
struct PointS { public int X; }
class PointC { public int X; }
var a = new PointS { X = 1 }; var b = a; b.X = 2; // a.X == 1, the value was copied
var c = new PointC { X = 1 }; var d = c; d.X = 2; // c.X == 2, the reference was copied
In Go you can see the compiler's decisions:
go build -gcflags=-m ./...
Stack overflow
The stack is limited, so very deep recursion or huge local arrays overflow it. It shows up differently:
- Java —
StackOverflowError; - JavaScript —
RangeError: Maximum call stack size exceeded; - Python —
RecursionError(the interpreter limits depth in advance); - C/C++ — usually a crash of the process.
How to fix it: turn recursion into a loop, use an explicit data structure (your own stack on the heap), and avoid large arrays in local variables.
Why it matters for performance
- Fewer heap allocations mean less GC work and shorter pauses.
- Reuse buffers in hot loops instead of creating new objects on every iteration.
- Compact data is faster: an array of values is traversed more efficiently than an array of references to scattered objects.
- Do not optimize blindly. Profile first, change second.