Skip to content

CONCEPT Cited by 1 source

V8 young generation

Definition

The young generation (aka "young space", "nursery") is the area of V8's heap where newly allocated objects live initially. It is compacted / collected frequently by a scavenging minor-GC, optimized for the observation that most JS objects die young.

Objects that survive several minor GCs get promoted to the old generation (also "old space") — a larger, less-frequently- compacted region with a different GC algorithm (mark-sweep- compact). Old-gen GC is substantially more CPU-intensive per byte than young-gen GC.

Size is a tunable knob — and the embedder sets it

V8 exposes the young-generation size to the embedder (Chrome, Node.js, Deno, Workers, etc.) as a tunable parameter. The embedder can either:

  • Set it manually. Historical guidance tied it to total memory budget (e.g. in 2017, "~512 MB environments use N MB young-gen"); embedders hand-tuned.
  • Let V8 choose heuristically. V8's internal heuristics have evolved significantly and, as of 2025, choose a reasonable value for most workloads.

Sizing tradeoff

  • Smaller young-gen → minor GCs run more frequently (less room between triggers) and more objects get promoted to old-gen faster (harder to collect later). CPU goes up.
  • Larger young-gen → minor GCs run less often, more objects die in young space where they're cheap to reclaim, fewer objects get promoted — but process memory usage is higher (young-gen occupies RAM).

The right size depends on allocation rate and object-lifetime distribution, which V8's heuristics can measure at runtime; embedder-chosen fixed caps miss this dynamic adjustment.

Canonical instance: Cloudflare Workers (2025-10)

Cloudflare Workers had hand-tuned young-gen size to V8's 2017 guidance for environments with ≤ 512 MB (Workers isolates default to 128 MB), on the reasoning that the isolate was small so the young-gen should be small. The value was set by Kenton Varda in June 2017, two months after Workers launched, when Kenton was the only engineer on the project.

By 2025, V8's GC had changed dramatically — the 2017 choice was holding young-gen too small, making GC work harder and more frequently than it needed to. When profiling the cf-vs-vercel-bench benchmarks, the GC load was 10–25 % of request processing time across Workers, Next.js, and React — unusual enough to prompt re-examination of the tuning.

Fix: removed the manual tuning, let V8 pick young-space size via its own internal heuristics.

Impact:

  • ~25 % improvement on the benchmarked workloads.
  • Small memory-usage increase (young-gen got bigger).
  • All Workers benefit, not just the benchmarked ones — though most Workers see a much smaller improvement because GC isn't their bottleneck.

"V8's entire garbage collector has changed dramatically since 2017. When analyzing the benchmarks, it became apparent that the setting which made sense in 2017 no longer made sense in 2025, and we were now limiting V8's young space too rigidly. Our configuration was causing V8's garbage collection to work harder and more frequently than it otherwise needed to." (Source: sources/2025-10-14-cloudflare-unpacking-cloudflare-workers-cpu-performance-benchmarks)

Lesson

When an embedder ships with a hand-tuned engine parameter, the engine's evolution can silently invalidate the choice. The tuning sits on the hot path of every allocation in every isolate, and its cost is invisible unless someone profiles across a workload that stresses allocation rate (e.g., a CPU + allocation-heavy benchmark that spends ~20 % of time in GC). Default-to-heuristic beats default-to-historical-manual once the engine provides credible heuristics.

Seen in

Last updated · 200 distilled / 1,178 read