Question presented to candidate: "Two allocation patterns exist in the same app: a request-scoped temporary object created and discarded thousands of times per second, and a long-lived cache object that survives the whole process. Does V8's garbage collector treat these the same way?"
What a strong answer should cover:
- V8's heap is split by generation: a young generation (
new_space) for newly allocated, typically short-lived objects, and an old generation (old_space) for objects that have survived multiple collections and are assumed likely to keep living — this is the generational hypothesis: most objects die young, so optimizing for that case pays off. - 📌 Verified, not just described:
v8.getHeapSpaceStatistics()confirmed real, distinct heap spaces genuinely exist in a running process (new_space,old_space, pluscode_spaceand others) — not an abstract textbook description. - Minor GC (Scavenge) runs frequently on the young generation, is fast, and copies surviving objects — 📌 verified directly with
--trace-gc: many realScavengeevents fired in quick succession during a real allocation loop, each completing in low single-digit milliseconds. - Major GC (Mark-Compact) runs on the old generation, less frequently, and is more expensive since it must trace the entire reachable object graph — 📌 verified directly: a real
Mark-Compactevent appeared in the same trace, reclaiming a large amount of memory (163.8MB → 49.5MB) in a single pass, structurally different from the many small Scavenge events around it. - An object surviving enough Scavenge cycles is promoted from the young generation to the old generation — this is exactly why the two example allocation patterns in the prompt are treated differently: the request-scoped temporary object is reclaimed cheaply by Scavenge long before ever qualifying for promotion, while the long-lived cache is promoted and then only revisited by the rarer, more expensive Mark-Compact pass.
- A precise answer names that GC pauses are a real, measurable cost — a large Mark-Compact pause can be a visible latency spike — and that this generational design exists specifically to minimize how often the expensive full-graph trace needs to run, not to eliminate GC pauses entirely.
Clarifying questions expected:
- "Is the concern understanding the mechanism, or diagnosing an actual observed GC-related latency/memory problem?" — the latter routes to the dedicated memory-leaks and heap-snapshot questions.
- "Does the interviewer want the generational model specifically, or V8 memory management more broadly (Buffers living outside the heap, covered elsewhere)?"
Code / implementation expected: Yes — a real --trace-gc log showing genuine Scavenge and Mark-Compact events is the concrete, convincing proof, not a description of the two-generation model.