README
22.3 Advanced Garbage Collection
Understanding V8's garbage collection helps you write memory-efficient code and avoid performance issues. This section covers the internals of V8's generational and incremental GC.
Table of Contents
- β’22.3 Advanced Garbage Collection
- β’Table of Contents
- β’Garbage Collection Fundamentals
- β’Reachability
- β’V8 Memory Structure
- β’Generational Garbage Collection
- β’Scavenger (Minor GC)
- β’Mark-Sweep-Compact (Major GC)
- β’Incremental and Concurrent GC
- β’Memory Leaks and Detection
- β’Optimization Strategies
- β’Key Takeaways
- β’Next Steps
Garbage Collection Fundamentals
JavaScript automatically manages memory. The garbage collector finds and frees unused objects.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MEMORY LIFECYCLE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β 1. ALLOCATION β
β const obj = { x: 1, y: 2 }; β
β const arr = [1, 2, 3, 4, 5]; β
β β
β 2. USE β
β console.log(obj.x); β
β arr.forEach(n => process(n)); β
β β
β 3. COLLECTION (Automatic) β
β // When obj and arr are no longer reachable β
β // GC reclaims the memory β
β β
β The Challenge: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β’ Must find ALL unreachable objects β β
β β β’ Must not free objects still in use β β
β β β’ Should minimize pause times β β
β β β’ Should be memory efficient β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Reachability
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β REACHABILITY CONCEPT β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ROOT OBJECTS (Always Reachable): β
β β’ Global object (window/global) β
β β’ Currently executing functions (call stack) β
β β’ Active closures β
β β
β Reachability Chain: β
β β
β ββββββββββ β
β β Roots β β
β ββββββ¬ββββ β
β β β
β ββββββΌββββ ββββββββββ ββββββββββ β
β β Obj A βββββββ Obj B βββββββ Obj C β β REACHABLE β
β ββββββββββ ββββββββββ ββββββββββ β
β β
β ββββββββββ ββββββββββ β
β β Obj X βββββββ Obj Y β β UNREACHABLE (no path from roots) β
β ββββββββββ ββββββββββ β
β β
β Example: β
β let user = { name: "Alice" }; // Reachable via 'user' β
β user = null; // Now unreachable - GC can free β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
V8 Memory Structure
V8 divides the heap into different spaces for efficient memory management.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β V8 HEAP STRUCTURE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β HEAP β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β NEW SPACE (Young Generation) ββ β
β β β ββββββββββββββββββββ ββββββββββββββββββββ ββ β
β β β β From-Space β β To-Space β ββ β
β β β β (Active) β β (Empty) β ββ β
β β β β 1-8 MB each β β β ββ β
β β β ββββββββββββββββββββ ββββββββββββββββββββ ββ β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β OLD SPACE (Old Generation) ββ β
β β β ββ β
β β β β’ Objects that survived young generation ββ β
β β β β’ Large objects (allocated directly here) ββ β
β β β β’ Size: Grows dynamically (up to heap limit) ββ β
β β β ββ β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β β
β β βββββββββββββββββββββ βββββββββββββββββββββ β β
β β β Code Space β β Map Space β β β
β β β (Compiled code) β β (Hidden classes) β β β
β β βββββββββββββββββββββ βββββββββββββββββββββ β β
β β β β
β β βββββββββββββββββββββ β β
β β β Large Object Spaceβ β β
β β β (Objects > 1MB) β β β
β β βββββββββββββββββββββ β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Memory Spaces Explained
| Space | Purpose | GC Type | Size |
|---|---|---|---|
| New Space | New objects | Minor GC (Scavenger) | 1-8 MB per semi-space |
| Old Space | Long-lived objects | Major GC (Mark-Sweep) | Dynamic |
| Code Space | JIT compiled code | Rarely collected | Dynamic |
| Map Space | Hidden classes | Collected with old space | Dynamic |
| Large Object Space | Objects > 1MB | Major GC | Dynamic |
Generational Garbage Collection
V8 uses the "generational hypothesis": most objects die young.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GENERATIONAL HYPOTHESIS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Object Lifetime Distribution: β
β β
β Objects β β
β Created ββββββββββββββββββββββ β
β βββββββββββββββ β
β βββββββββ β
β ββββββ β
β ββββ β
β βββ β
β ββ β
β ββ β
β ββ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β Short Medium Long Object Lifespan β
β β
β Key Insight: β
β β’ Most objects (80-90%) die very young β
β β’ Few objects live for a long time β
β β’ Optimize for the common case (short-lived objects) β
β β
β Strategy: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β YOUNG GENERATION OLD GENERATION β β
β β β’ Small, fast GC β’ Large, slower GC β β
β β β’ Runs frequently β’ Runs less often β β
β β β’ Scavenge algorithm β’ Mark-Sweep-Compact β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Scavenger (Minor GC)
The Scavenger handles the young generation using a copying algorithm.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SCAVENGE ALGORITHM β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β BEFORE SCAVENGE: β
β ββββββββββββββββββββββββββ ββββββββββββββββββββββββββ β
β β From-Space β β To-Space β β
β β ββββββββββββββββββββ β β (Empty) β β
β β β A ββ B ββ C ββ D β β β β β
β β ββββββββββββββββββββ β β β β
β β ββββββββββ β β β β
β β β E ββ F β β β β β
β β ββββββββββ β β β β
β ββββββββββββββββββββββββββ ββββββββββββββββββββββββββ β
β β
β Assume: A, C, E are reachable; B, D, F are garbage β
β β
β AFTER SCAVENGE: β
β ββββββββββββββββββββββββββ ββββββββββββββββββββββββββ β
β β From-Space β β To-Space β β
β β (Empty) β β βββββββββββββββ β β
β β β β β A ββ C ββ E β β β
β β β β βββββββββββββββ β β
β β β β β β
β β β β Survivors copied β β
β β β β Compacted together β β
β ββββββββββββββββββββββββββ ββββββββββββββββββββββββββ β
β β
β Then: Spaces are SWAPPED β
β β’ To-Space becomes new From-Space β
β β’ Old From-Space becomes new To-Space β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Object Promotion
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β OBJECT PROMOTION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Objects are PROMOTED to Old Space when: β
β β
β 1. They survive TWO scavenges β
β 2. To-Space is more than 25% full β
β β
β First Scavenge: β
β βββββββββββββββ βββββββββββββββ β
β β From-Space β β To-Space β β
β β βββββ β βββ β βββββ β β
β β β A β β copy β β A β β A survives (age = 1) β
β β βββββ β β βββββ β β
β βββββββββββββββ βββββββββββββββ β
β β
β Second Scavenge: β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β From-Space β β To-Space β β Old Space β β
β β βββββ β β β β βββββ β β
β β β A β β βββββββββββββββββββββββ β β A β β PROMOTEDβ
β β βββββ β β βββββ β β
β βββββββββββββββ βββββββββββββββ β
β β
β Promotion Benefits: β
β β’ Long-lived objects don't get copied repeatedly β
β β’ Young GC stays fast (fewer objects to scan) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Scavenger Characteristics
| Aspect | Details |
|---|---|
| Algorithm | Cheney's copying algorithm |
| Speed | Very fast (1-2ms typical) |
| Frequency | Often (when new space fills) |
| Stop-the-world | Yes, but very short pause |
| Memory overhead | 2x (two semi-spaces) |
| Fragmentation | None (compacting by nature) |
Mark-Sweep-Compact (Major GC)
Major GC handles the old generation using mark-sweep-compact algorithm.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MARK-SWEEP-COMPACT β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β PHASE 1: MARK β
β βββββββββββββββ β
β Start from roots, mark all reachable objects β
β β
β Roots β
β β β
β βΌ β
β βββββ βββββ βββββ β
β β A βββββββ B βββββββ C β β Marked (reachable) β
β βββββ βββββ βββββ β
β β β β β
β β
β βββββ βββββ β
β β X β β Y β β Not marked (garbage) β
β βββββ βββββ β
β β
β PHASE 2: SWEEP β
β ββββββββββββββββ β
β Free memory of unmarked objects β
β β
β βββββ βββββ βββββ βββββ βββββ β
β β A β β B β β β β C β β β β
β βββββ βββββ βββββ βββββ βββββ β
β β β FREE β FREE β
β β
β PHASE 3: COMPACT (Optional) β
β ββββββββββββββββββββββββββ β
β Move objects to eliminate fragmentation β
β β
β BEFORE: β A β β B β β C β β β
β AFTER: β A β B β C β β FREE β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Tri-Color Marking
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TRI-COLOR MARKING β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Three Colors: β
β β’ WHITE: Not yet visited (potentially garbage) β
β β’ GRAY: Visited, but children not scanned β
β β’ BLACK: Visited and all children scanned β
β β
β Algorithm: β
β β
β 1. Initially all objects are WHITE β
β 2. Roots are marked GRAY β
β 3. While GRAY objects exist: β
β a. Pick a GRAY object β
β b. Mark its WHITE children GRAY β
β c. Mark the object BLACK β
β 4. All WHITE objects are garbage β
β β
β Visual: β
β β
β Step 1: ββββββββββ (all white) β
β β β
β Root β
β β
β Step 2: ββββββββββ (root is gray) β
β β
β Step 3: ββββββββββ (root black, child gray) β
β β
β Step 4: ββββββββββ (continue marking) β
β β
β Step 5: ββββββββββ (one object remains white = garbage) β
β β
β Legend: β = WHITE, β = GRAY, β = BLACK β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Incremental and Concurrent GC
V8 uses various techniques to reduce pause times.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PAUSE TIME STRATEGIES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β STOP-THE-WORLD (Blocking) β
β βββββββββββββββββββββββββ β
β JavaScript βββββββββββββββββββββββββββββββββββββββββββ β
β GC ββββββββββββ β
β ββ Pause ββ β
β β
β Problem: Long pauses (100ms+) cause jank β
β β
β INCREMENTAL MARKING β
β ββββββββββββββββββ β
β JavaScript ββββββββββββββββββββββββββββββββββββββββββ β
β GC β β β β β β β
β β Small increments β β
β β
β Mark in small chunks interleaved with JavaScript β
β β
β CONCURRENT MARKING β
β βββββββββββββββββ β
β JavaScript ββββββββββββββββββββββββββββββββββββββββββ β
β GC (thread) βββββββββββββββββββββββββββββ β
β ββββ Marking in parallel ββββ β
β β
β Mark on a separate thread while JavaScript runs β
β β
β PARALLEL SCAVENGING β
β ββββββββββββββββββ β
β JavaScript βββββββββββββββββββββββββββββββββββββββββββ β
β GC Thread 1 βββ β
β GC Thread 2 βββ β
β GC Thread 3 βββ β
β ββ Short pause, parallel work β β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
V8's Orinoco GC
V8's modern garbage collector (Orinoco) combines multiple techniques:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ORINOCO GC FEATURES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β 1. PARALLEL SCAVENGING β
β β’ Multiple threads evacuate young generation β
β β’ Reduces minor GC pause time β
β β
β 2. CONCURRENT MARKING β
β β’ Marking happens on background threads β
β β’ Main thread continues executing JavaScript β
β β’ Only short atomic pause for finalization β
β β
β 3. INCREMENTAL MARKING β
β β’ Mark objects in small steps β
β β’ Interleaved with JavaScript execution β
β β’ Write barriers track mutations β
β β
β 4. LAZY SWEEPING β
β β’ Sweep pages lazily as memory is needed β
β β’ Spread sweeping work over time β
β β
β 5. CONCURRENT COMPACTION β
β β’ Move objects on background thread β
β β’ Update pointers incrementally β
β β
β Timeline Example: β
β β
β Main Thread βββββββββββββββββββββββββββββββββββββββββββ β
β Worker 1 ββββββββββββββββββββββββββββββββββββββββββ β
β Worker 2 ββββββββββββββββββββββββββββββββββββββββββ β
β βββ Concurrent marking βββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Memory Leaks and Detection
Common patterns that cause memory leaks in JavaScript.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β COMMON MEMORY LEAKS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β 1. FORGOTTEN EVENT LISTENERS β
β β
β element.addEventListener('click', handler); β
β // Later: element removed, but handler still referenced β
β // Fix: element.removeEventListener('click', handler); β
β β
β 2. DETACHED DOM NODES β
β β
β const nodes = []; β
β function onClick() { β
β const div = document.createElement('div'); β
β nodes.push(div); // div is retained even if removed β
β } β
β β
β 3. CLOSURES OVER LARGE SCOPES β
β β
β function process() { β
β const hugeData = loadHugeData(); β
β return function() { β
β // Closure keeps hugeData alive! β
β return doSomething(); β
β }; β
β } β
β β
β 4. GROWING CACHES β
β β
β const cache = {}; β
β function getData(key) { β
β if (!cache[key]) { β
β cache[key] = fetchData(key); // Never cleaned up! β
β } β
β return cache[key]; β
β } β
β β
β 5. CIRCULAR REFERENCES (Historical - modern GCs handle this) β
β β
β const a = {}; β
β const b = {}; β
β a.ref = b; β
β b.ref = a; // Not a leak in modern V8 β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Detecting Memory Leaks
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LEAK DETECTION STRATEGIES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β 1. CHROME DEVTOOLS HEAP SNAPSHOTS β
β β
β Take multiple snapshots β Compare β Find growing objects β
β β
β Snapshot 1: 10 MB β
β Snapshot 2: 15 MB (+5 MB - investigate!) β
β Snapshot 3: 20 MB (+5 MB - likely leak!) β
β β
β 2. ALLOCATION TIMELINE β
β β
β Record allocations over time β
β Blue bars = allocated, Gray = freed β
β Growing blue = leak β
β β
β βββββββββββββββββββββββββββββββββββ β
β Normal: allocate, then freed β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
β Leak: allocations not freed β
β β
β 3. NODE.JS FLAGS β
β β
β node --expose-gc --trace-gc script.js β
β process.memoryUsage() β
β β
β 4. PERFORMANCE.MEMORY (Chrome) β
β β
β console.log(performance.memory.usedJSHeapSize); β
β // Monitor over time β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Optimization Strategies
Strategy 1: Object Pooling
// Instead of creating many short-lived objects:
// BAD
function process(data) {
return { x: data.x * 2, y: data.y * 2 }; // New object each time
}
// GOOD: Reuse objects from a pool
class VectorPool {
constructor(size = 100) {
this.pool = Array.from({ length: size }, () => ({ x: 0, y: 0 }));
this.index = 0;
}
acquire(x, y) {
const obj = this.pool[this.index];
this.index = (this.index + 1) % this.pool.length;
obj.x = x;
obj.y = y;
return obj;
}
}
Strategy 2: Avoid Closures Over Large Data
// BAD: Closure retains large data
function createProcessor(hugeData) {
return function process(index) {
return hugeData[index]; // hugeData never freed
};
}
// GOOD: Only capture what's needed
function createProcessor(hugeData) {
const lookup = new Map(); // Extract needed data
hugeData.forEach((item, i) => lookup.set(i, item.value));
return function process(index) {
return lookup.get(index);
};
}
Strategy 3: Bounded Caches
// BAD: Unbounded cache
const cache = {};
// GOOD: Use LRU cache with limit
class LRUCache {
constructor(maxSize = 100) {
this.maxSize = maxSize;
this.cache = new Map();
}
get(key) {
if (this.cache.has(key)) {
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value); // Move to end
return value;
}
return undefined;
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.maxSize) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey); // Remove oldest
}
this.cache.set(key, value);
}
}
Strategy 4: WeakMap for Object Metadata
// BAD: Strong reference prevents GC
const metadata = new Map();
function setMeta(obj, data) {
metadata.set(obj, data); // obj can't be GC'd!
}
// GOOD: WeakMap allows GC
const metadata = new WeakMap();
function setMeta(obj, data) {
metadata.set(obj, data); // obj can be GC'd
}
Key Takeaways
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GC SUMMARY β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β GENERATIONAL GC: β
β β’ Young generation: Small, fast, frequent GC (Scavenger) β
β β’ Old generation: Large, slower, less frequent GC (Mark-Sweep) β
β β’ Objects promoted after surviving 2 scavenges β
β β
β MODERN V8 (ORINOCO): β
β β’ Parallel: Multiple GC threads β
β β’ Concurrent: GC runs alongside JavaScript β
β β’ Incremental: Work spread over time β
β β’ Result: Minimal pause times β
β β
β AVOID MEMORY LEAKS: β
β β
Remove event listeners when done β
β β
Clear references to detached DOM nodes β
β β
Avoid closures over large data β
β β
Use bounded caches (LRU) β
β β
Use WeakMap/WeakSet for object metadata β
β β
β OPTIMIZE ALLOCATIONS: β
β β
Reuse objects (object pooling) β
β β
Avoid creating objects in hot loops β
β β
Pre-allocate arrays when size is known β
β β
Prefer primitives over objects when possible β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Next Steps
Continue to 22.4 ES Modules Internals to understand how JavaScript modules are loaded, parsed, and executed.