Memory management
Reason about reachability, garbage collection, retained objects, weak collections, and evidence-driven leak diagnosis.

WHAT YOU WILL BE ABLE TO DO
Learning outcomes
- Explain memory management in plain language.
- Connect the behavior to the underlying browser or framework model.
- Implement the core pattern and reason through edge cases.
- Answer common follow-ups without relying on memorized phrases.
01Overview
JavaScript engines use mark-and-sweep garbage collection: starting from roots (globals, the current stack), the engine marks everything reachable and frees everything else. Memory leaks in JS are really unintentional reachability — something keeps a reference alive longer than intended.
02Mental model
- Detached DOM nodes: a removed element is still reachable (and unreleasable) if a variable or closure still references it.
- Forgotten timers/intervals/listeners: a setInterval or addEventListener that's never cleared keeps its closure — and everything it captured — alive indefinitely.
- Unbounded caches: a Map or object used as a cache with no eviction policy grows forever.
03Examples
function attach(el) { const bigData = new Array(1_000_000).fill('x'); const handler = () => console.log(bigData.length); el.addEventListener('click', handler); return () => el.removeEventListener('click', handler); // call this on cleanup}04Check understanding
A single-page app's memory grows on every route change even though components unmount. What's the likely cause?
A listener, timer, or subscription set up per route isn't being torn down in the corresponding cleanup, so each mount adds another live closure the garbage collector can't reclaim.
DDConcept deep dives
Deep dive 1
Reachability, not reference counting, is the useful model
Garbage collectors begin from roots and trace reachable values. An unreachable cycle can be collected, while one unintended path from a global cache or active listener can retain a large graph. Eligibility does not mean immediate collection; engines schedule and optimize collection based on their own heuristics.
- High reserved heap is not proof of a leak.
- A leak is unwanted continued reachability across the application's expected lifecycle.
- Finalization timing must never be required for core program correctness.
Deep dive 2
Common leaks are missing ownership boundaries
Timers, event listeners, observers, subscriptions, unresolved work, detached DOM references, and unbounded collections all need a clear owner and release condition. Framework unmounting removes managed DOM, but it cannot automatically clean external resources created without returning or invoking their teardown.
- Register and release resources in paired lifecycle code.
- Bound caches by count, cost, or expiry instead of assuming eventual collection.
- Abort obsolete work to release closures and reduce wasted computation.
Deep dive 3
Heap evidence beats guessing
Design a repeatable interaction cycle, allow or request garbage collection in tooling, compare snapshots, and find object types whose retained counts grow after every cycle. A retaining-path view shows the chain back to a root. Fix the earliest unintended owner and repeat the identical experiment to prove the graph is released.
- Allocation timelines reveal which interaction created growing objects.
- Detached nodes are clues, not automatic proof of a leak.
- WeakMap is useful for metadata keyed by object lifetime, but it does not repair other strong references.
QAInterview questions and model answers
Attempt each answer aloud before opening it. The model answer shows the depth and precision expected in an interview; it is not a script to memorize.
Intermediate · Conceptual · 1 min · Question 1How does JavaScript garbage collection decide what to reclaim?Open model answer
Model answer
Modern collectors primarily determine reachability from roots such as active execution state and host references. Objects that cannot be reached are eligible for collection even if they participate in cycles. Collection timing is intentionally nondeterministic.
Open question page →Intermediate · Conceptual · 1 min · Question 2What is a memory leak in garbage-collected JavaScript?Open model answer
Model answer
It is memory that remains reachable even though the application no longer needs it. Common causes include unbounded caches, forgotten listeners or timers, retained detached DOM nodes, observers, and closures captured by long-lived owners.
Open question page →Advanced · Coding · 1 min · Question 3Why do WeakMap keys not prevent collection?Open model answer
Model answer
A WeakMap does not keep its object keys strongly reachable. When a key has no other strong path from a root, its entry may disappear. Weak collections are non-enumerable because collection timing cannot become observable.
const meta = new WeakMap();let node = document.createElement('div');meta.set(node, { renderedAt: Date.now() });
node = null; // nothing else references the div -> // the div AND its WeakMap entry become collectibleIntermediate · Conceptual · 1 min · Question 4How would you diagnose a browser memory leak?Open model answer
Model answer
Reproduce a stable interaction cycle, record heap allocation or compare snapshots, trigger collection where tooling permits, and inspect retaining paths for object types whose counts keep growing. Fix the ownership path and repeat the same experiment.
Open question page →Intermediate · Coding · 1 min · Question 5What is a detached DOM tree?Open model answer
Model answer
It is a DOM subtree removed from the document but still reachable from JavaScript, perhaps through a listener, closure, cache, or framework reference. Being detached is not itself a leak; continued unintended reachability is.
const cache = [];function render() { const el = document.createElement('div'); cache.push(el); // keeps every `el` alive forever document.body.append(el); el.remove(); // detached from the document, still in `cache`}Intermediate · Conceptual · 1 min · Question 6Should cleanup set every local variable to null?Open model answer
Model answer
No. Locals naturally become unreachable when their environment is no longer retained. Cleanup should release long-lived ownership—unsubscribe, disconnect observers, clear timers, abort work, and bound caches—rather than ritualistically nulling short-lived variables.
Open question page →Advanced · Conceptual · 1 min · Question 7What is generational garbage collection?Open model answer
Model answer
It organizes objects by observed lifetime so short-lived values can be collected frequently while older survivors are scanned differently. Exact strategies remain engine implementation details.
Open question page →Advanced · Conceptual · 1 min · Question 8Why are WeakMap entries not enumerable?Open model answer
Model answer
Enumeration would expose whether garbage collection has occurred, making nondeterministic collection timing observable and preventing important implementation freedom.
Open question page →Advanced · Conceptual · 1 min · Question 9Can an unresolved promise leak forever?Open model answer
Model answer
A promise and attached handlers are collectible when nothing reachable refers to them. It becomes a leak when a long-lived owner, pending host operation, or captured resource retains the chain.
Open question page →Advanced · Conceptual · 1 min · Question 10What is the purpose of FinalizationRegistry?Open model answer
Model answer
It can schedule best-effort cleanup notifications after collection, but timing and execution are not guaranteed. Core resource correctness must use explicit disposal instead.
Open question page →SCScenario questions
Scenario 1
A single-page dashboard grows by 20 MB after every route visit. Describe a reliable investigation.
- Automate repeated navigation so every sample performs the same work.
- Compare heap snapshots after garbage collection.
- Group growing constructors and inspect their retaining paths.
- Audit route cleanup for subscriptions, observers, timers, and caches.
- Verify the plateau after the fix.
Reveal worked answer
I would not diagnose from one high memory number because collectors reserve heap. Repeated route cycles plus post-GC snapshots reveal monotonic retention. The retaining path identifies the real owner—often a global subscriber, observer, timer, or cache. After cleanup I expect object counts to return near baseline and memory to plateau across cycles.