Call Stack & Memory Model (Stack vs Heap)
📖 Concept
Understanding how the call stack and memory work is essential for debugging, writing efficient code, and understanding recursion.
The Call Stack:
- A LIFO (Last In, First Out) data structure managed by the JavaScript engine
- Every function call pushes a stack frame containing: local variables, arguments, return address
- When a function returns, its frame is popped off the stack
- JavaScript is single-threaded — one call stack, one thing at a time
Stack vs Heap:
- Stack memory: Fast, automatic, limited size. Stores primitives and function references. LIFO order. Auto-cleaned when function returns.
- Heap memory: Larger, slower, manually collected (by GC). Stores objects, arrays, closures. No particular order. Cleaned by garbage collector.
What goes where?
Stack: Heap:
├─ number: 42 ├─ { name: "Alice", age: 30 }
├─ boolean: true ├─ [1, 2, 3, 4, 5]
├─ string ref → ──────────├─ "Hello World"
├─ object ref → ──────────├─ function() { ... }
├─ return address └─ closure scope
└─ ...
Stack Overflow: When the call stack exceeds its maximum size (usually 10,000-25,000 frames). Common cause: infinite recursion.
Garbage Collection: JavaScript uses mark-and-sweep — starting from root references, it marks all reachable objects and sweeps (deletes) unreachable ones. Understanding this helps avoid memory leaks.
Memory Leaks in JavaScript:
- Global variables that never get cleaned
- Forgotten event listeners
- Closures that hold references to large objects
- Detached DOM nodes
🏠 Real-world analogy: The stack is like a stack of plates — you add and remove from the top (LIFO). The heap is like a warehouse — items are placed wherever there's space, and a cleaner (GC) periodically removes items nobody needs anymore.
💻 Code Example
1// CALL STACK VISUALIZATION2function third() {3 console.log("third"); // Stack: [main, first, second, third]4 // When third() returns, its frame is popped5}6function second() {7 third(); // Stack: [main, first, second] → pushes third8 console.log("second");9}10function first() {11 second(); // Stack: [main, first] → pushes second12 console.log("first");13}14first(); // Stack: [main] → pushes first1516// STACK OVERFLOW — exceeding call stack limit17function infiniteRecursion() {18 return infiniteRecursion(); // Never stops!19}20// infiniteRecursion(); // RangeError: Maximum call stack size exceeded2122// STACK VS HEAP — primitives vs objects23let a = 10; // Stack: a = 1024let b = a; // Stack: b = 10 (COPY of value)25b = 20; // a is still 10! Primitives are copied2627let obj1 = { x: 1 }; // Stack: obj1 → ref to Heap object28let obj2 = obj1; // Stack: obj2 → SAME ref in Heap29obj2.x = 99; // obj1.x is also 99! Objects share references3031// MEMORY LEAK EXAMPLE — closure holding reference32function createLeak() {33 const hugeData = new Array(1000000).fill("data");34 return function() {35 // This closure keeps 'hugeData' alive forever!36 console.log(hugeData.length);37 };38}39const leakyFn = createLeak(); // hugeData can never be GC'd4041// PREVENTING MEMORY LEAKS42function noLeak() {43 const hugeData = new Array(1000000).fill("data");44 const length = hugeData.length; // Extract what you need45 return function() {46 console.log(length); // Only keeps the number, not the array47 };48}4950// TypeScript — understanding value vs reference types51function demonstrateMemory(): void {52 // Primitives (stack) — passed by value53 let x: number = 5;54 let y: number = x;55 y = 10;56 console.log(x); // 5 — unchanged5758 // Objects (heap) — passed by reference59 const arr1: number[] = [1, 2, 3];60 const arr2: number[] = arr1; // Same reference!61 arr2.push(4);62 console.log(arr1); // [1, 2, 3, 4] — both changed!6364 // Deep copy to avoid shared references65 const arr3: number[] = [...arr1]; // New array in heap66 arr3.push(5);67 console.log(arr1); // [1, 2, 3, 4] — unchanged68}
🏋️ Practice Exercise
Practice Problems:
- Draw the call stack for:
first()callingsecond()callingthird() - What happens in memory when you do
let a = {x:1}; let b = a; b.x = 2;? - Write code that causes a stack overflow and catch the error
- Explain why recursive fibonacci uses O(n) space even though it makes O(2ⁿ) calls
- Identify the memory leak in a given code snippet with closures
- What's the maximum call stack depth in your browser? Write code to measure it
- Explain the difference between shallow copy and deep copy in terms of heap memory
- Why are strings sometimes said to be "on the stack" even though they're objects?
- How does JavaScript's garbage collector know when to free memory?
- Write a function that demonstrates pass-by-value vs pass-by-reference
⚠️ Common Mistakes
Thinking objects are stored on the stack — objects are ALWAYS on the heap; only the reference (pointer) is on the stack
Not understanding that assigning an object to a new variable copies the REFERENCE, not the object — both point to the same heap memory
Thinking JavaScript has manual memory management — it uses automatic garbage collection (mark-and-sweep)
Forgetting that closures keep outer scope variables alive — this can cause memory leaks if large objects are captured
Confusing the call stack with the event loop — the call stack handles synchronous execution; the event loop handles async callbacks
💼 Interview Questions
🎤 Mock Interview
Practice a live interview for Call Stack & Memory Model (Stack vs Heap)