Memory Leak Sources
Four classic causes of a JavaScript memory leak — forgotten listeners, uncleared timers, long-lived closures, and detached DOM nodes still referenced.
Purpose
A memory leak happens when something keeps a reference to data alive longer than intended, so the garbage collector — which only frees unreachable memory — can never reclaim it. Four sources account for most JavaScript memory leaks in practice.
The four sources
- Forgotten event listener:
addEventListenerwith no matchingremoveEventListenerkeeps whatever the listener’s closure captured alive indefinitely, even after the element or feature it was meant for is gone. - Uncleared timer:
setInterval(or a chain ofsetTimeoutcalls) that’s never cleared keeps its closure — and everything it captured — alive for as long as the timer keeps running, often forever. - Long-lived closure: a closure attached to something that outlives its intended purpose (recall Execution Model, Scope, and Closures) — the general case the first two are specific instances of.
- Detached DOM: a removed element that’s no longer in the page, but still referenced from JavaScript (a cached lookup variable, a closure) — the element, and everything it in turn references, stays in memory.
Behavior notes
- A memory-snapshot comparison — one taken, some action repeated several times, another taken — is the standard way to confirm a suspected leak: a specific object count or “detached node” count that keeps growing across identical repeated actions, rather than stabilizing, indicates something is holding a reference it shouldn’t.
WeakMap/WeakSet(recall Map, Set, WeakMap, and WeakSet) exist specifically to attach data to an object without preventing its collection — a direct fix for some detached-reference patterns.
Common mistakes
- Adding an event listener in a component’s setup without a matching removal in its teardown.
- Caching a DOM element reference outside the scope that manages its lifecycle, outliving the element’s actual presence on the page.
Runtime notes
Garbage collection behavior is engine-specific in its details, but reachability — the principle these leaks all violate — is defined by ECMAScript and conceptually identical everywhere.