Garbage Collection

Garbage Collection and Memory Leaks

The garbage collector (GC) frees every object that is no longer reachable from the roots: the global object, the call stack and handles held by the browser. A JavaScript memory leak is therefore an unwanted reference that keeps an object reachable.

V8 86,723 's collector, Orinoco, is generational because most objects die young. A fast Scavenger copies live objects between two semi-spaces of the small young generation; survivors of two scavenges move to the old generation, which a mark-sweep-compact collector processes less often, marking concurrently on helper threads.

Reachability and generations in V8: unreachable objects are reclaimed, survivors are promoted
Reachability and generations in V8: unreachable objects are reclaimed, survivors are promoted

The most common leak is a cache keyed by objects that outlive their use. A Map holds its keys strongly; a WeakMap (Map and WeakMap) lets the GC drop an entry once nothing else references its key. Node's --expose-gc flag exposes gc() so pending garbage does not blur the measurement:

A Map cache leaks, a WeakMap cache does not (run: node --expose-gc leak.mjs)JavaScript
const heapMB = () => { globalThis.gc(); return (process.memoryUsage().heapUsed / 1e6).toFixed(1); };
console.log('baseline', heapMB(), 'MB');
const strong = new Map();                          // holds its keys strongly
for (let i = 0; i < 100_000; i++) strong.set({ id: i }, new Array(50).fill(i));
console.log('Map     ', heapMB(), 'MB,', strong.size, 'entries');
strong.clear();
const weak = new WeakMap();                        // holds its keys weakly
for (let i = 0; i < 100_000; i++) weak.set({ id: i }, new Array(50).fill(i));
console.log('WeakMap ', heapMB(), 'MB');
Output
baseline 3.8 MB
Map      55.7 MB, 100000 entries
WeakMap  4.0 MB
Common JavaScript memory leaks
Leak pattern Kept alive by Fix
Unbounded Map cache The collection WeakMap or LRU limit
Forgotten setInterval Callback closure clearInterval()
Listener on window Listener closure AbortSignal option
Detached DOM nodes JS variables Null the reference
Finding a leak with heap snapshots

In Chrome 1 's Memory panel, take a Heap snapshot, repeat the suspect action (open and close a dialog) several times, and take another. View the second in Comparison mode: constructors whose count grows with the repetitions are the leak. Sort by Retained Size (memory freed if the object died, versus Shallow Size, the object alone) and read the Retainers pane to find the variable, listener or closure holding it. The filter Objects retained by detached nodes isolates removed elements still referenced from JavaScript; in Node.js 2,131 , v8.writeHeapSnapshot() writes a file the same panel loads. Avoid WeakRef and FinalizationRegistry (WeakRef) for cleanup: GC timing is unspecified.