What are generations in GC (young, old, metaspace)?
4. Metaspace → set MaxMetaspaceSize 5. Generational ZGC (Java 21+) — best choice for low-latency apps on Java 21+. For older versions, use G1.
Junior Level
GC divides memory into generations based on how long objects live.
Simple analogy: Imagine a wardrobe:
- Young Gen — like new clothes: quickly worn out and thrown away.
- Old Gen — like a favorite jacket: you wear it for years.
- Metaspace — like patterns for sewing clothes. Not the clothes themselves, but the templates they’re created from.
Three generations:
| Generation | What’s stored | How often GC runs |
|---|---|---|
| Young Generation | New objects | Very often (every few seconds) |
| Old Generation | Long-lived objects | Rarely (every few minutes) |
| Metaspace | Class metadata | When full |
Object lifecycle:
new Object() → Eden (Young)
↓ (survived GC)
Survivor (Young)
↓ (survived several more GCs)
Old Generation
Middle Level
Weak Generational Hypothesis
Two observations:
- For most web applications, ~90-98% of objects die young. But for tasks with large caches, this is not the case.
- Objects that survive several collections live long
Consequence: Different algorithms for different generations!
Young Generation
Structure:
Eden (80%) — where all new objects go
Survivor S0 (10%) — survivors
Survivor S1 (10%) — survivors
Minor GC:
1. Eden + S0 → scanned
2. Live objects → copied to S1
3. Eden and S0 cleared
4. S0 and S1 swap roles
TLAB (Thread Local Allocation Buffer):
Each thread gets its own piece of Eden
→ Allocation without synchronization!
→ Just pointer shift
Old Generation
Objects get here:
1. Survived 15 collections (MaxTenuringThreshold)
2. Large objects (directly to Old)
3. When Survivor overflows
Full GC — cleans Young + Old
→ Long pause (seconds!)
Metaspace
Java 8+: replaced PermGen
→ Located in Native Memory (outside Heap)
→ Stores: classes, methods, constants
→ Grows dynamically
Limits:
-XX:MetaspaceSize=256m (initial threshold)
-XX:MaxMetaspaceSize=512m (maximum)
Card Table Optimization
Problem: Minor GC doesn't want to scan entire Old Gen
Solution: Card Table
→ Old Gen split into 512-byte cards
→ oldObj.field = youngObj → card marked Dirty
→ Minor GC scans only Dirty cards
Senior Level
TLAB and PLAB
TLAB (Thread Local Allocation Buffer):
→ In Eden, for each thread
→ Pointer bumping: obj_ptr += size
→ 0 synchronization!
PLAB (Promotion Local Allocation Buffer):
→ Analog in Old Gen
→ For copying surviving objects
→ Avoids contention during promotion
SATB (Snapshot-At-The-Beginning)
G1 uses SATB for concurrent marking:
→ At marking start: all live objects are marked
→ If reference removed during marking → object still considered alive
→ Floating Garbage: garbage that will be collected in the next cycle
Write Barrier for SATB:
→ Before writing reference: save old value to buffer
→ Buffer processed by Concurrent Marking
Humongous Objects (G1)
Humongous Objects — objects larger than half a G1 region size.
Allocated directly in Old Gen, bypassing Young Gen.
Remembered Sets (RSet) — data structure in G1 that tracks
inter-region references. Allows GC to avoid scanning entire Heap.
Object > 50% of region size → Humongous
→ Directly to Old Gen
→ Not copied in Young GC
→ Can cause premature GC
Solution: -XX:G1HeapRegionSize=16m or 32m
Generational ZGC (Java 21+)
ZGC became generational!
→ Young and Old generations
→ Young GC more often and faster
→ Old GC less often
→ -50% CPU overhead vs single-generational
Production Experience
Premature Promotion:
Survivor too small → objects pushed to Old Gen
→ Old Gen fills with garbage
→ Full GC every 5 minutes
Solution: increase SurvivorRatio or Young Gen
Best Practices
- TLAB — allocation without contention
- Card Table — Minor GC doesn’t scan Old Gen
- Humongous → increase G1HeapRegionSize
- Metaspace → set MaxMetaspaceSize
- Generational ZGC (Java 21+) — best choice for low-latency apps on Java 21+. For older versions, use G1.
Senior Summary
- Young Gen = copying collector, fast collections
- Old Gen = mark-sweep-compact, slow collections
- Metaspace = native memory, ClassLoader lifecycle
- TLAB = lock-free allocation
- Card Table = Minor GC optimization
- SATB = concurrent marking in G1
- Humongous = special regions for large objects
- Generational ZGC = future of low-latency GC
Interview Cheat Sheet
Must know:
- Weak Generational Hypothesis: ~90-98% of objects die young → different algorithms for generations
- Young Gen: Eden (80%) + Survivor S0 (10%) + Survivor S1 (10%); Minor GC copies live between Survivors
- Old Gen: objects that survived 15 collections (MaxTenuringThreshold); Full GC cleans Young + Old
- Metaspace (Java 8+): native memory, stores class metadata; replaced PermGen (was inside Heap)
- TLAB — thread-private buffer in Eden for lock-free allocation (pointer bumping)
- Card Table — bit map; on
oldObj.field = youngObjwrite, card marked Dirty → Minor GC doesn’t scan entire Old Gen - Premature Promotion: Survivor overflows → objects pushed to Old Gen → Full GC
Common follow-up questions:
- Why is Young Gen copying, while Old Gen is mark-sweep? — In Young Gen, 98% is garbage, copying 2% is efficient; in Old Gen, little garbage, copying would be more expensive
- What is SATB? — Snapshot-At-The-Beginning: G1 algorithm for concurrent marking; marks everything that was alive at cycle start
- Why 2 Survivor areas? — Ping-pong: live objects copied S0→S1→S0, increasing age; one is always empty for copying
- Why is Metaspace outside Heap? — PermGen (inside Heap) caused frequent OOM; Metaspace in native memory, grows dynamically
Red flags (DO NOT say):
- “Metaspace and PermGen are the same” — PermGen inside Heap (fixed), Metaspace in native memory (dynamic)
- “Objects get to Old Gen only through age” — Large objects and Survivor overflow also
- “Minor GC cleans Old Gen” — Minor GC only Young Gen; Card Table is optimization, not full cleanup
Related topics:
- [[4. What is Garbage Collection]]
- [[9. What is Young Generation]]
- [[10. What is Old Generation (Tenured)]]
- [[11. What is Metaspace (or PermGen)]]
- [[12. What GC algorithms exist]]