In this chapter
We'll learn the two areas where a running program keeps its data — the stack (a tidy pile of trays, cleaned up automatically) and the heap (a big warehouse that keeps things until they're cleared) — plus the call stack, local variables and dynamic allocation.
The Problem in Real Life
John opens the seat-hold service code and points at one function, holdSeat. It runs every time a fan picks a seat — on a busy day, about 300,000 times.
"Every time this runs, it creates some data," he says. "Some of that data disappears the moment the function finishes. Some of it stays behind. We need to know which is which."
So some memory cleans itself up, and some doesn't? How do I tell the difference?
Anna
One Pile of Memory vs. Two Very Different Areas
Two areas, two rules
Short-lived data and long-lived data are stored in different places with different rules.
Automatic cleanup — or not
One area cleans itself up when a function ends. The other keeps things until they're no longer needed.
The leak lives in one of them
Knowing where data lives tells you where a leak can hide.
The Stack and the Heap
A running program splits its memory into two main working areas. Each has a picture that makes it easy to remember.
- The stack — a pile of trays in a cafeteria: each time a function is called, a fresh tray is put on top of the pile. On that tray go the function's local variables (Act 07) and a note saying where to go back to when it's done. When the function finishes, its tray is taken off the top and everything on it is gone — instantly and automatically. The stack is small, very fast and perfectly tidy. It's the same last-in, first-out idea as the stack of plates in Act 09.
- The call stack — the trays in action: when
holdSeatcallscheckSeat, a tray forcheckSeatgoes on top of the tray forholdSeat. WhencheckSeatreturns, its tray comes off, andholdSeatcontinues where it left off. The pile of trays at any moment is called the call stack, and each tray is a stack frame. When an error happens, the "stack trace" you see (Act 17) is simply a list of the trays that were on the pile at that moment. And if functions keep calling functions forever, like recursion without a base case in Act 09, the pile reaches the ceiling: stack overflow. - The heap — a big warehouse: some data has to outlive the function that created it, or is too big or too unpredictable for a tray: a list of seats, an object describing a hold, a timer. That goes into the heap — a large, flexible storage area. Things in the heap stay there until they're cleaned up (next chapter). The heap is bigger and more flexible than the stack, but a little slower, and it doesn't clean itself up when a function ends.
- How the two work together: when
holdSeatcreates a hold object, the object itself goes into the heap warehouse. The local variable on the stack tray holds only a reference to it — a note saying "the hold is on shelf 4,021." When the function ends, the tray and its note are gone. Whether the hold object in the warehouse can also be thrown away depends on one thing: does anything else still have a note pointing to shelf 4,021?
| Feature | Stack (pile of trays) | Heap (warehouse) |
|---|---|---|
| What goes there | Local variables and call information | Objects, arrays, anything long-lived or sized at run time |
| Size | Small | Large |
| Speed | Very fast | A bit slower |
| Cleanup | Automatic when the function returns | Only when nothing references it (garbage collection) or manually |
| Typical problem | Stack overflow (too many trays) | Memory leak (things never cleared) |
| Moment | Trays on the pile (top first) |
|---|---|
| holdSeat starts | holdSeat |
| holdSeat calls checkSeat | checkSeat, holdSeat |
| checkSeat calls findSeat | findSeat, checkSeat, holdSeat |
| findSeat returns | checkSeat, holdSeat |
| checkSeat returns | holdSeat |
| holdSeat returns | (empty) |
One call to holdSeat
Stack: tray for holdSeat
seatId, now, a reference → hold
Heap: hold object
{ seatId, fanId, heldUntil }
Heap: 10-minute timer
will release the seat
Function returns
the tray is removed instantly
Heap objects stay
as long as anything still references them — like the timers map
const timers = new Map(); // created once at startup — lives in the heap all dayfunction holdSeat(seatId, fanId) {const now = Date.now(); // stack: local value, gone when the function endsconst hold = { // heap: a new objectseatId,fanId,heldUntil: now + HOLD_DURATION_MS,};const timer = setTimeout(() => releaseSeat(seatId), HOLD_DURATION_MS); // heap: a timertimers.set(seatId, timer); // the long-lived map now references the timerreturn hold;}
When holdSeat returns, now, hold and timer (the names) disappear from the stack. The timer object itself stays, because the timers map still points to it.
Dynamic allocation — asking for space while running: often a program can't know in advance how much memory it needs. How many seats will this event have — 200 or 20,000? How many holds today? So it asks for memory while it runs, exactly when it needs it. That's called dynamic allocation, and the space comes from the heap. Object allocation is the everyday case: every time JavaScript runs { ... }, [ ... ] or new Something(), it allocates a new object in the heap.
In the seat-hold service: Anna traces one call of holdSeat. A tray goes on the stack with local variables seatId and now. A new hold object { seatId, fanId, heldUntil } is allocated in the heap. A timer is created to release the seat after 10 minutes — also in the heap. Then the function returns; the tray disappears. But two things are still in the warehouse: the hold object and the timer. And on line 31, she spots it: timers.set(seatId, timer). A long-lived map, created when the service starts, is storing a note to every timer. Forever?
Key Takeaway
The stack is a pile of cafeteria trays: each function call gets a tray for its local variables, removed automatically when the function returns — small, fast and tidy. The heap is a big warehouse for data that must outlive a function or whose size isn't known in advance; it doesn't clean itself up when functions end. Stack variables usually hold references to objects that live in the heap.
Why This Matters
Almost every memory problem is about the heap: something put in the warehouse and never cleared. Knowing that local variables vanish but heap objects stay as long as they're referenced lets you look at any code and ask the right question: "what long-lived thing is still holding a note to this?" Stack overflows, stack traces and recursion all make sense once you picture the pile of trays.
Anna has a suspect: a map that keeps a note to every timer. But JavaScript is supposed to clean up memory by itself — that's what everyone says. So why isn't it cleaning these up? To answer that, she needs to know how memory gets cleaned at all.
