Memory Management

How JavaScript allocates, uses, and frees memory automatically.

Memory Lifecycle

Three stages: allocate → use → release.

1. Allocate

Reserve memory

2. Use

Read / Write

3. Release

Free memory

// 1. Allocate:
const name = "Alice";      // allocates memory for string
const nums = [1, 2, 3];   // allocates memory for array
const user = { age: 28 }; // allocates memory for object

// 2. Use:
console.log(name);         // reads from memory
nums.push(4);              // writes to memory

// 3. Release:
// JavaScript does this AUTOMATICALLY
// via Garbage Collection (GC)
// You don't call free() or delete manually

Stack vs Heap

Two types of memory with different purposes.

Stack Memory

  • • Primitives (numbers, strings, booleans)
  • • Function call frames
  • • References (pointers to heap)
  • • Fixed size, fast access
  • • Auto-freed when scope ends

Heap Memory

  • • Objects, arrays, functions
  • • Dynamic size
  • • Slower access than stack
  • • Freed by Garbage Collector
  • • Can grow as needed
let age = 28;           // stack: stores 28 directly
let name = "Alice";     // stack: reference → heap: "Alice"

let user = { age: 28 }; // stack: reference → heap: { age: 28 }
let arr = [1, 2, 3];    // stack: reference → heap: [1, 2, 3]

// When variables go out of scope:
// Stack values are immediately freed
// Heap values wait for garbage collection

Garbage Collection

The engine automatically frees unreachable memory.

// Mark-and-Sweep algorithm:
// 1. Start from "roots" (global, stack variables)
// 2. Mark all objects reachable from roots
// 3. Sweep (free) all unmarked objects

let user = { name: "Alice" }; // reachable ✓
user = null; // { name: "Alice" } is now unreachable
// GC will free it on next cycle

The main algorithm: if no live variable can reach an object, it's garbage and gets freed.

💡 Unreachable = eligible for collection
1 / 3

Memory Leaks

When memory can't be freed because references accidentally persist.

1. Forgotten event listeners

// Leak: listener holds reference to large data
function setup() {
  const hugeData = new Array(1000000);
  element.addEventListener("click", () => {
    console.log(hugeData.length); // holds hugeData!
  });
}
// Fix: remove listener when done
// element.removeEventListener("click", handler);

2. Global variables

// Leak: accidentally creating globals
function process() {
  result = data.map(x => x * 2); // missing 'const'!
  // 'result' is now global and never freed
}
// Fix: always use const/let

3. Detached DOM nodes

// Leak: reference to removed DOM element
const button = document.getElementById("btn");
button.remove(); // removed from DOM
// But 'button' variable still holds a reference!
// The DOM node can't be GC'd
// Fix: button = null;

4. Closures holding large data

function createHandler() {
  const bigArray = new Array(1000000).fill("x");
  return () => {
    // Only uses bigArray.length but holds ALL of it
    return bigArray.length;
  };
}
// Fix: extract what you need before closure
// const len = bigArray.length;
// return () => len;

Best Practices

Write memory-efficient JavaScript.

Use const/let, never implicit globals
Remove event listeners when components unmount
Nullify references to large objects when done
Use WeakMap/WeakSet for caches (allow GC)
Clear intervals/timeouts when no longer needed
Avoid storing unnecessary data in closures

FAQ

Common questions about memory.