Lesson 19 of 30
Most performance problems are about one page load or one interaction being slow. A memory leak is a different failure mode entirely: a session that gets progressively worse the longer it stays open.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 3
A dashboard SPA feels fine right after being opened but noticeably janky after being left open for a few hours, with no single obvious slow action. What kind of problem does this symptom most point to?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Everything else in this track is about a single moment: a slow first paint, a slow response to one click, a layout shift on one load. A memory leak breaks that framing — it's not about any single moment being slow, it's about a long-lived single-page-application session getting progressively slower and more janky the longer it stays open. A dashboard that feels snappy when a user opens it in the morning and noticeably sluggish by the afternoon, with no single code change in between, is the signature symptom.
The mechanism (covered in the JavaScript track's garbage collection lesson) is reachability: JavaScript keeps anything still reachable from a live reference, and frees anything that isn't. A leak is something staying reachable longer than intended. What that means for a long session specifically is that the pool of "reachable but never actually needed again" memory keeps growing, and the garbage collector has to work harder against a bigger and bigger live set every time it runs, which itself becomes a source of jank — a GC pause that stalls the main thread mid-interaction.
// 1. Listener on a long-lived target, never removed
function ChatPanel() {
useEffect(() => {
window.addEventListener("resize", handleResize);
// missing: return () => window.removeEventListener("resize", handleResize);
}, []);
}
// 2. Interval that outlives the component that started it
function LiveTicker() {
useEffect(() => {
setInterval(fetchLatestPrice, 1000);
// missing: clearInterval cleanup on unmount
}, []);
}
// 3. A closure capturing more than it uses
function attachHandler(hugeDataset) {
const total = hugeDataset.length; // only this is needed
button.addEventListener("click", () => {
console.log(total);
});
// the closure's enclosing scope still holds a live reference to
// hugeDataset itself, keeping the whole dataset reachable
}
All three share the same shape: something outlives the component or moment that created it, and stays reachable through a reference nothing is actively using anymore. In a page that loads once and reloads on navigation, this barely matters — the whole heap gets thrown away. In a long-lived SPA session where components mount and unmount hundreds of times without a full page reload, each unreleased listener, interval, or over-captured closure is one more thing pinned in memory for the rest of the session.
setInterval is an easy one to miss because the bug is an omission, not a mistake in the code that's there. There's no wrong line to spot — the bug is the missing clearInterval in the cleanup path, which only shows up as absence."A long-running dashboard gets slower the longer it's left open — how would you track that down?" is a real, practical question, and describing the two-snapshot heap comparison workflow (not just naming "memory leak" as the diagnosis) is what shows you can actually find one, not just recognize the symptom.