JavaScript Interview Prep
JS Engine & Execution

Memory Heap & Garbage Collection

Where Your Objects Live and Die

LinkedIn Hook

You never call malloc() or free() in JavaScript. Memory just... happens. But that's a trap.

Behind the scenes, the JS engine splits memory into two regions: the Stack (for primitives and function frames) and the Heap (for objects, arrays, and functions). A garbage collector walks the heap, marks everything still reachable from the roots, and sweeps away the rest. Anything unmarked — gone.

The problem? Your code can quietly keep objects "reachable" long after you stop needing them: a forgotten setInterval, a removed DOM node still held by a variable, a global created by a missing let. All four common leak patterns trap the garbage collector.

In this lesson you'll learn the Mark-and-Sweep algorithm, the 4 classic leak patterns, and how ES2021's WeakRef lets you hold a reference that doesn't block GC.

If you've ever had a Node process quietly eat 2GB of RAM — this is the lesson that explains why.

Read the full lesson -> [link]

#JavaScript #InterviewPrep #GarbageCollection #MemoryLeak #JSEngine #Performance #WebDevelopment


Memory Heap & Garbage Collection thumbnail


What You'll Learn

  • The difference between Stack and Heap memory in JavaScript
  • How Mark-and-Sweep garbage collection actually works
  • The 4 most common memory-leak patterns and how to fix each
  • What WeakRef and FinalizationRegistry are for

Two Regions of Memory

JavaScript manages memory automatically. You don't need to malloc() or free() like in C. But understanding how it works helps you avoid memory leaks.

Two Parts of Memory

AreaWhat's StoredStructure
Call StackPrimitives (number, string, boolean, etc.), function calls, referencesOrdered, LIFO
Memory HeapObjects, arrays, functions (anything complex)Unstructured, large pool

Code Example — Stack vs Heap

// Primitives -> stored on the STACK
let a = 10;
let b = a;    // b gets a COPY of a's value
b = 20;
console.log(a); // 10 (unchanged — separate copy)

// Objects -> stored on the HEAP
let obj1 = { name: "Rakibul" };
let obj2 = obj1;  // obj2 gets a COPY of the REFERENCE (pointer)
obj2.name = "Hassan";
console.log(obj1.name); // "Hassan" (CHANGED — same object!)

Garbage Collection — Mark & Sweep Algorithm

JavaScript uses the Mark & Sweep algorithm:

  1. Mark Phase: Start from "roots" (global object, call stack variables). Follow all references and mark every reachable object.
  2. Sweep Phase: Go through ALL objects in the heap. Anything not marked gets deleted.
function createUser() {
  let user = { name: "Rakibul", scores: [90, 85, 92] };
  return user.name; // only the string is returned
}

let result = createUser();
// After createUser() returns:
// - `user` variable is gone (popped off stack)
// - The object { name: "Rakibul", scores: [...] } has NO references
// - Garbage collector will SWEEP it away
// - The returned string "Rakibul" is still alive (referenced by `result`)

Common Memory Leak Patterns

1. Accidental Global Variables

function leak() {
  // Missing 'let/const' -> creates a global variable!
  oops = "I'm stuck on window forever";
}
leak();
// window.oops exists -> never garbage collected

2. Forgotten Timers

// This creates a memory leak
const data = loadHugeData();
setInterval(() => {
  // `data` is captured in this closure — can never be GC'd
  // even if you no longer need it
  process(data);
}, 1000);

// Fix: clear the interval when done
const timer = setInterval(() => process(data), 1000);
clearInterval(timer); // Now `data` can be GC'd

3. Detached DOM Nodes

let button = document.getElementById("myButton");
document.body.removeChild(button);
// The DOM node is removed from the page BUT
// `button` still holds a reference -> not garbage collected!

button = null; // Now it can be GC'd

4. Closures Holding Large Data

function outer() {
  const hugeArray = new Array(1000000).fill("data");
  
  return function inner() {
    // inner() closes over `hugeArray` even if it never uses it
    // in some engines (older V8), the entire closure scope is retained
    console.log("Hello");
  };
}

const fn = outer(); // hugeArray may stay in memory

WeakRef and FinalizationRegistry (ES2021)

// WeakRef: holds a reference that doesn't prevent garbage collection
let obj = { data: "important" };
let weakRef = new WeakRef(obj);

console.log(weakRef.deref()); // { data: "important" }

obj = null; // now the object CAN be garbage collected

// Later...
console.log(weakRef.deref()); // undefined (if GC has run)

Memory Heap & Garbage Collection visual 1


Common Mistakes

  • Forgetting to clearInterval / clearTimeout when a component unmounts — the closure keeps captured data alive forever.
  • Holding a JS reference to a DOM node after removing it from the page — the node is "detached" but not GC'd.
  • Omitting let/const inside a function and accidentally creating a global that never goes away.
  • Assuming const helps with memory — it only prevents re-binding; the object it points to is still on the heap and GC'd only when no references remain.

Interview Questions

Q: What's the difference between Stack and Heap memory in JavaScript?

The Stack stores primitives (numbers, strings, booleans, etc.) and function frames in a strict LIFO order. The Heap stores objects, arrays, and functions in an unstructured pool. When you assign an object to a variable, the stack holds a reference (pointer) into the heap.

Q: Where are primitives vs objects stored in memory?

Primitives live on the stack, objects/arrays/functions live on the heap. That's why let b = a with primitives creates a copy, but with objects creates a shared reference.

Q: How does garbage collection work in JavaScript?

Using the Mark-and-Sweep algorithm. The GC starts from roots (global object, stack variables), marks every object reachable through references, then sweeps the heap and frees anything unmarked.

Q: Explain the Mark & Sweep algorithm.

Mark Phase: walk from the roots and flag every reachable object. Sweep Phase: traverse the heap and free any object that wasn't flagged. The flags are then cleared for the next cycle.

Q: Name 3 common causes of memory leaks in JavaScript.

  1. Accidental globals — forgetting let/const creates variables on window that persist forever.
  2. Forgotten timers/callbackssetInterval or event listeners that capture large data in closures and are never cleared.
  3. Detached DOM references — removing a DOM node from the page but keeping a JS variable pointing to it.

Q: What is a WeakRef and when would you use it?

WeakRef creates a reference to an object that doesn't prevent garbage collection. Useful for caches where you want the data available if it exists but don't want to force it to stay in memory. Call .deref() to access the value — it returns undefined if the object was collected.


Quick Reference — Cheat Sheet

MEMORY MODEL — QUICK MAP

  STACK                        HEAP
  -----                        ----
  primitives (number, str...)  objects
  function frames              arrays
  references (pointers)        functions
  LIFO, fast                   unstructured, larger

  GARBAGE COLLECTION (Mark & Sweep)
    1. Mark   -> from roots, flag all reachable objects
    2. Sweep  -> free every unflagged object on heap

  COMMON LEAKS
    1. Accidental globals (missing let/const)
    2. Forgotten setInterval / event listeners
    3. Detached DOM nodes still held by JS vars
    4. Closures retaining large data

  WeakRef (ES2021): reference that does NOT block GC
  FinalizationRegistry: run a callback when object is GC'd

Previous: var vs let vs const Next: Lexical Scope


This is Lesson 1.6 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.

On this page