By Rahul — Google Frontend Engineer
The Basics
JavaScript automatically manages memory. When you create objects, arrays, or strings, memory is allocated. When they are no longer reachable, the garbage collector frees the memory. You do not call free() like in C — but you can still cause memory leaks.
Reachability
The GC starts from "roots" (global object, currently executing function's variables, closures) and marks everything reachable from them. Anything not reachable is garbage.
let user = { name: 'Rahul' }; // Object is reachable via 'user'
user = null; // Object is now unreachable → garbage collectedMark-and-Sweep (Modern V8)
- Mark phase: Starting from roots, mark all reachable objects
- Sweep phase: Free memory of all unmarked objects
- Compact: Move remaining objects together to reduce fragmentation
Generational Collection in V8
V8 divides memory into two areas:
- Young Generation (1-8MB): Newly created objects. Collected frequently with a fast "Scavenger". Most objects die young
- Old Generation (up to GBs): Objects that survived multiple young-gen collections. Collected less frequently with Mark-Sweep-Compact
Common Memory Leak Patterns
Forgotten Timers
// LEAK: interval runs forever, holds reference to heavyData
const heavyData = loadLargeDataset();
setInterval(() => {
processData(heavyData);
}, 1000);
// FIX: clear when done
const id = setInterval(() => { ... }, 1000);
clearInterval(id); // When component unmountsDetached DOM Nodes
// LEAK: removed from DOM but still referenced in JS
const button = document.getElementById('btn');
document.body.removeChild(button);
// 'button' variable still holds reference → not GC'd
// FIX: null the reference
button = null;Closures Holding Large Data
function createProcessor() {
const cache = new Map(); // Grows without limit
return function process(key, data) {
if (!cache.has(key)) {
cache.set(key, expensiveCompute(data));
}
return cache.get(key);
};
}
// FIX: Use WeakMap or set a max cache sizeWeakRef and FinalizationRegistry
// WeakRef — does not prevent garbage collection
const cache = new Map();
function getImage(url) {
const ref = cache.get(url);
if (ref) {
const img = ref.deref(); // Returns undefined if GC'd
if (img) return img;
}
const img = loadImage(url);
cache.set(url, new WeakRef(img));
return img;
}Debugging Memory Leaks
- Chrome DevTools → Memory tab → Take Heap Snapshot
- Compare snapshots before and after a suspected leak
- Look for growing "Detached DOM trees" and increasing object counts
- Use
performance.measureUserAgentSpecificMemory()for production monitoring
Summary
V8 uses generational mark-and-sweep garbage collection. Most objects die young. Memory leaks happen from forgotten timers, detached DOM nodes, and unbounded caches. Use Chrome DevTools Memory tab to find leaks. Use WeakMap/WeakSet for caches that should not prevent GC.