Under The Hood
backend nodejs Node.js 20+ (V8 GC, Orinoco)

V8 GC: Scavenge (Semi-Space Young Gen) vs Mark-Sweep-Compact (Old Gen)

Last updated
Prerequisites:
Node.js memory basics
Understanding of GC concepts (young/old generations)
  • nodejs
  • v8
  • garbage-collection
  • scavenger
  • mark-compact
  • memory

Read at your depth

The practical view

V8 (the JS engine in Node.js) divides the heap into generations. The young generation uses a semi-space (from-space/to-space) collector called Scavenger: most objects die young, so it quickly copies surviving objects to the to-space and frees the from-space. Objects surviving multiple scavenges are promoted to the old generation. The old generation is collected with Mark-Sweep-Compact (when needed): mark reachable objects, sweep unreachable ones, and optionally compact to reduce fragmentation. The two generations are why Node memory usage grows in steps and why 'minor GC' (scavenge) is much cheaper than 'major GC'.

The same idea in other frameworks

react equivalent
N/A — V8 runtime concern.

Legacy vs modern

Allocation churn vs stable references

Creating short-lived objects per request triggers constant scavenges; reusing/long-lived structures reduce young-gen pressure.

before → after
Per-request allocation churn
function handle(req) {
  const result = { data: req.body.split(',').map(Number) }; // ❌ OLD: new objects per request
Reuse buffers/objects where practical
// ✅ NEW: reuse buffers and avoid per-request temp strings where cheap

Interview gotchas

Context

This tests the generational model — interviewers want the semi-space copy vs old-gen mark/sweep/compact mechanics and the frequency/cost asymmetry that defines V8's pause profile.

The mechanical answer

Scavenge is a minor GC on the young generation: a semi-space collector that copies the small set of surviving objects from from-space to to-space (and promotes survivors), then swaps the spaces. Because most objects die young, the survivor set is small, so scavenge is fast (a few ms or less) and frequent (happens as the nursery fills — often many times per second under allocation load). Mark-Compact is a major GC on the old generation: it marks all reachable objects from roots (tracing the whole heap), sweeps the unreachable ones, and optionally compacts (moving objects to defragment). It traverses the entire old heap, so it's much more expensive and much rarer (triggered by old-gen allocation pressure or heap-limit proximity). The asymmetry is why V8 uses a generational design: cheap frequent cleaning for short-lived garbage, expensive rare cleaning for long-lived data.

Trap

The naive answer is 'GC pauses are always bad'. Minor GCs are cheap and frequent; major GCs are expensive and rare — tuning targets the major GCs. Another trap: assuming old-gen objects are never collected — they are, but only by major GC, so long-lived caches grow until a major GC reclaims them. Candidates who describe the two generations and their cost asymmetry show real V8 understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.