skip to content

Explain generational garbage collection and the weak generational hypothesis. How do minor and major collections differ?

level: middleimportance: should knowfreq 66%

answer

  1. Weak generational hypothesis: most objects die young; few old→young refs
  2. Young = Eden + 2 survivor spaces; Old = tenured
  3. Minor GC: young only, fast, frequent, copies survivors, ages → promote
  4. Major/Full GC: old gen, slower, rarer
  5. Card table / remembered set + write barrier track old→young refs

basics

~20 s

Most objects die young, so the heap is split into a young generation (new objects) and an old generation (survivors). A minor GC collects only the young area (fast, frequent); a major/full GC handles the old area (slower, rarer). Objects that survive get promoted to the old generation.

solid answer

~50 s

Generational GC exploits the weak generational hypothesis: the vast majority of objects die young, and references from old objects to young ones are rare. HotSpot splits the heap into a young generation (Eden plus two survivor spaces) and an old/tenured generation. New objects go into Eden; a minor GC copies the few survivors between survivor spaces and reclaims the rest cheaply — because most are dead, collecting only the young area is fast and can run frequently. Objects that survive enough minor GCs are promoted (tenured) to the old generation, which fills slowly and is collected by a major/full GC that scans much more and is slower. To collect young alone without scanning the whole old gen, the collector tracks old→young references via a card table / remembered set, so those few cross-generational pointers act as extra roots for the minor collection. This generational design is why typical short-lived allocations are nearly free.

go deeper

for a junior

Knows the heap has a young and old generation, that new objects start young, and that minor GC is frequent/fast while full GC is rarer/slower.

for a middle

States the weak generational hypothesis, describes Eden/survivor/old layout, the copy-and-age-then-promote flow, and the minor vs major/full distinction.

for a senior

Adds card table/remembered set + write barrier for old→young tracking, tenuring thresholds, promotion-pressure tuning, and how this maps to GC logs and leak signatures.

for a principal

Reasons about collector trade-offs (throughput vs pause: Parallel/G1/ZGC/Shenandoah), region sizing, allocation-rate-driven pause budgets, and when generational assumptions break (large long-lived working sets, off-heap strategies).

## The observation it's built on: the weak generational hypothesis Decades of measurement show two things about object lifetimes: 1. **Most objects die young** — temporaries, loop locals, request-scoped objects are created and discarded almost immediately. 2. **Old objects rarely point to young ones** — long-lived structures don't often gain references to brand-new objects. This is the **weak generational hypothesis**. If most objects die young, then *concentrating collection effort on the youngest objects* reclaims the most garbage for the least work. That's the entire rationale for **generational GC**. ## Splitting the heap into generations HotSpot divides the **heap** into: - **Young generation**, itself split into: - **Eden** — where almost all new objects are allocated. - **Two survivor spaces** (S0 and S1) — small areas that hold objects surviving a young collection; one is always empty between collections. - **Old (tenured) generation** — for objects that have lived long enough. (Metaspace, for class metadata, is separate and **not** part of the heap.) ## Minor GC — collecting the young generation When **Eden fills up**, a **minor GC** runs: 1. It finds the **live** objects in Eden + the in-use survivor space (using reachability from GC roots, *plus* cross-generational references — see below). 2. It **copies** those survivors into the *other* survivor space (or promotes them), and then **wipes Eden and the old survivor space wholesale**. 3. Because most objects are dead, there are few survivors to copy → this is **fast** and amortizes allocation to nearly a pointer bump. Each object carries an **age** (number of minor GCs survived). Once age crosses a **tenuring threshold**, the object is **promoted (tenured)** into the old generation. (Objects too big for Eden may be allocated straight into old gen.) This copying-collector style is why young-gen GC is cheap: cost is proportional to **live** data, not total data, and live data is small. ## Major / Full GC — collecting the old generation The **old generation** fills slowly (only promoted survivors land there). When it's full (or near a threshold), a **major GC** (or **full GC**, which does old + young + sometimes metaspace) runs. It scans a much larger, denser region of mostly-live objects, so it is **slower and causes longer pauses**. The design goal is to make these rare. Different collectors handle the old gen differently — mark-sweep-compact (Parallel), concurrent marking (G1, CMS historically), or mostly-concurrent low-pause designs (ZGC, Shenandoah). ## The cross-generation problem: card tables / remembered sets There's a subtlety: to collect the young gen *alone*, the GC must find all references **into** the young gen — including those from **old-gen objects** — without scanning the entire (huge) old generation. Solution: the JVM tracks where old→young references exist using a **card table** (the old gen is divided into small "cards"; a **write barrier** dirties a card whenever an old object's reference field is updated) or a **remembered set**. During a minor GC, only the **dirty cards** are scanned, and those old→young pointers are treated as **additional GC roots** for the young collection. This keeps minor GC cheap and is why the second half of the hypothesis (few old→young refs) matters: it keeps the remembered set small. ## Why it matters in practice - **Allocation is cheap**, collection of short-lived garbage is cheap → favor immutable, short-lived objects rather than object pooling in most cases. - **Promotion pressure**: if objects survive minor GCs they don't need to (e.g. held in a cache too long, or survivor space too small causing premature promotion), the old gen fills faster → more expensive major GCs. Sizing young vs old gen is a real tuning lever. - Connects directly to **OOM diagnosis**: a leak shows up as old-gen occupancy that never drops after full GCs. ## Tie-in to this topic Generational GC is a language-neutral JVM-platform mechanism (it applies to any JVM language). It builds on **reachability/GC roots** (the live-set definition) and explains the **young/old heap layout** from the memory-areas question — and its failure mode is the heap **OutOfMemoryError** you diagnose with heap dumps and GC logs.

  • How does a minor GC avoid scanning the entire old generation for references into the young gen?
    It uses a card table / remembered set maintained by a write barrier: whenever an old-gen object's reference field is updated, the corresponding card is marked dirty. The minor GC scans only dirty cards and treats the old→young references found there as extra GC roots, so cost stays proportional to young-gen size.
  • What is object promotion (tenuring) and why can premature promotion hurt?
    Promotion moves an object from young to old gen after it survives a tenuring threshold of minor GCs. Premature promotion (e.g. survivor space too small) pushes still-short-lived objects into old gen, filling it faster and triggering more expensive major/full GCs.

saying these in an interview costs you the question

  • Saying a minor GC scans the whole heap (it scans only young + cross-gen refs)
  • Confusing major GC (old gen) with full GC (everything) loosely without nuance — or claiming minor GC collects old objects
  • Thinking object pooling is generally beneficial — young-gen allocation is usually cheaper
  • Ignoring how old→young references are tracked (card table/remembered set)

context