Ch. 1 · JavaScript

JavaScript Memory Leaks and Retained References

JavaScript Memory Leaks and Retained References. Learn the reasoning, a practical example, common mistakes and an interview exercise.

~2 min readadvancedupdated Oct 3, 2026

Garbage collection cannot reclaim reachable objects. Leaks arise when listeners, timers or caches retain values beyond their intended lifetime.

Step-by-step walkthrough

Step 1: Define the expected lifetime

Name the owner of the view, listener, timer and cached data. A leak investigation starts by identifying what should become unreachable after teardown. Memory that remains intentionally reachable for a bounded cache is not automatically a defect.

Step 2: Find the retaining path

A global listener can hold a callback that closes over a removed view. A timer or Map can do the same. Removing a DOM element does not remove every external reference to its model, so inspect the actual path from a long-lived root.

Step 3: Verify repeated recovery

Run multiple create-and-destroy cycles and compare retained objects after collection where the diagnostic tool permits it. Look for repeated surviving instances and their owners, not only a single high memory sample. The fix should break the unintended reference on every exit path.

Worked scenario

A window listener closes over a removed view’s model. Removing the listener releases that retention path.

function mountStatus(model) {
  const handler = () => console.log(model.id);
  window.addEventListener('resize', handler);
  const timer = setInterval(handler, 1000);
  return function dispose() {
    window.removeEventListener('resize', handler);
    clearInterval(timer);
  };
}
// const dispose = mountStatus(model);
// Call dispose when this view's ownership ends.
JavaScript

Walk through the example

Both resources retain the handler, and the handler retains model. Teardown must release both routes. Keeping the exact handler reference is necessary to remove the listener. The returned dispose function also closes over the model, so the caller should release its own obsolete cleanup reference when the view is gone.

Common mistake

One heap sample includes allocation churn and collection timing; growth alone does not identify the owner.

Verify the behavior

Repeat mounting and disposal, trigger resize and advance timers, then confirm old handlers do not execute. Investigate retaining paths in heap snapshots for removed models. Include failed initialization and early navigation so cleanup is not tested only on a successful lifecycle.

Interview exercise

Verify a removed view is collectible.

Answer and reasoning

Repeat mount-unmount cycles, compare snapshots after collection and inspect retaining paths. Confirm the fix across repeated cycles.

Follow-up discussion

Are cycles always leaks? Unreachable cycles can be collected; reachability from a long-lived owner is the important issue. Should every cache be cleared immediately? No, but caches need bounded lifetime and size so intentional retention does not become uncontrolled growth.

Continue learning

Compare the scenario with the JavaScript interview questions and test your understanding with the JavaScript MCQs. For terminology and implementation details, consult the reference material.

More in JavaScript

read ✓JavaScript · mid

JavaScript Date and Timezone Pitfalls

Parse and format dates without timezone surprises: zero-based months, date-only versus date-time parsing, and why UTC storage avoids drift.

~3 min readread →
read ✓JavaScript · mid

JavaScript Number Precision and BigInt

Why 0.1 + 0.2 is not 0.3, where safe integer range ends, and when BigInt is the right tool for large identifiers and money.

~2 min readread →
esc