The JavaScript engine
How browser memory leaks happen
Recognize listeners, timers, closures, collections, and detached DOM nodes that keep unwanted objects reachable.
8 minute lesson
A JavaScript memory leak is usually not memory the collector missed. It is memory the program accidentally keeps reachable.
const removedPanels = []
function closePanel(panel) {
panel.remove()
removedPanels.push(panel)
}
The panel is detached from the document, but the array still references it. The panel and everything reachable through it remain in memory.
Common retaining references include:
- event listeners registered on long-lived objects
- intervals, observers, and subscriptions that were not stopped
- closures captured by callbacks
- caches without size or lifetime limits
- collections of detached DOM nodes
Give every long-lived resource an owner and a cleanup point:
function mountPanel(panel) {
const controller = new AbortController()
window.addEventListener('resize', updatePanel, {
signal: controller.signal
})
return () => controller.abort()
}
To investigate, repeat a lifecycle rather than staring at one memory number: open a view, close it, and do that several times. Take heap snapshots before and after. If instances accumulate, inspect their retaining paths. The useful evidence is the reference keeping an object alive, because that points to the cleanup code that is missing.
Some growth is intentional caching, and garbage collection timing is nondeterministic. A leak diagnosis needs repeatable retention after the feature should have released its data.
Lesson completed