Glossary / Memory leaks
Memory leaks
A JavaScript memory leak occurs when objects remain reachable longer than the application intends, preventing garbage collection. Common causes include listeners that are never removed, timers that continue running, subscriptions without unsubscribe, unbounded caches, detached DOM nodes retained by closures, and global registries that accumulate entries.
Diagnosing retained memory
- Establish a repeatable workload and baseline.
- Exercise the suspected path repeatedly, then return to an equivalent idle state.
- Capture heap snapshots and compare retained objects after collection where developer tools allow.
- Inspect retaining paths: what reference keeps each unexpectedly large object reachable?
- Fix lifecycle ownership, repeat the workload, and confirm retained growth stops.
A single high heap reading is not proof of a leak; the runtime may delay collection or retain memory for reuse. Look for a growing retained baseline after equivalent workloads. Heap snapshots can contain sensitive application data, so handle them carefully.
Use AbortController for cancellable work and event-listener lifetimes, clear timers, unsubscribe from publishers, bound caches by size/TTL, and dispose objects when their owner ends. Do not call garbage collection or use weak references as a substitute for ownership design. Node server leaks and browser leaks may have different roots and diagnostics.
Interview questions
- How distinguish a leak from allocation pressure? Repeated workload and retained-heap growth after equivalent idle/collection points.
- What is a retaining path? The chain of references keeping an object reachable from a root.
- Why can detached DOM still consume memory? A listener or closure may retain it after removal from the document.
- What is the durable fix? Correct lifecycle cleanup and bounded ownership, not forced GC.