keyboard-shortcut
d

Stack and heap memory allocation

3min read

Stack and heap memory allocation

The short version:

  • The stack stores short-lived, fixed-size data tied to function calls.
  • The heap stores data whose size or lifetime is not known locally.
  • Stack allocation is usually cheaper; heap allocation is more flexible.
  • Python hides this choice. Rust makes it visible through types such as Box, String, and Vec.

These are useful models, not guarantees: compilers and runtimes may optimise where data actually lives.

Stack

Each function call gets a stack frame containing things such as arguments, local values, and a return address. Frames are removed in reverse order when calls return, so allocation is little more than moving a pointer.

The stack is fast and automatically managed, but limited in size. Very deep recursion can overflow it.

Heap

Heap memory can outlive the function that requested it and can hold dynamically sized values. Allocating and releasing it requires more bookkeeping; ownership, garbage collection, or reference counting decides when it can be reused.

Rust: the distinction is visible

fn main() {
    let count: i32 = 3;                 // fixed-size value; normally stack
    let name = String::from("pelican"); // String metadata on stack, text on heap
    let boxed = Box::new(42);            // Box pointer on stack, 42 on heap

    println!("{count} {name} {boxed}");
}

Rust releases name and boxed when their owners leave scope. No garbage collector is needed. Moving ownership can change which variable controls heap data without copying that data.

Python: the distinction is hidden

def total() -> int:
    numbers = [1, 2, 3]
    return sum(numbers)

In CPython, the function call has an interpreter frame, while the list and integers are Python objects managed on the heap. numbers is a reference to the list, not the list stored directly in a conventional stack slot.

Python normally reclaims objects through reference counting, with a cyclic garbage collector as backup. That convenience adds overhead but removes most manual memory decisions.

Practical rule

In Python, focus on object lifetimes, copies, and container sizes. In Rust, also notice whether a type owns heap data and when that ownership moves. Reach for heap allocation when values must grow, escape their scope, or have an unknown size—not because “heap” or “stack” is inherently better.