What is G1 GC
The Garbage-First (G1) Garbage Collector is the default server-class Garbage Collector in the HotSpot JVM (enabled by default since Java 9). It is engineered for multi-gigabyte...
🟢 Junior Level
The Garbage-First (G1) Garbage Collector is the default server-class Garbage Collector in the HotSpot JVM (enabled by default since Java 9). It is engineered for multi-gigabyte heaps, offering a high-throughput, soft real-time collector with predictable, user-configurable pause times.
Real-World Analogy
Think of a large logistics shipping warehouse:
- Legacy collectors treated the warehouse as two massive rooms: a small sorting dock (Young Gen) and a massive storage hall (Old Gen). Cleaning the storage hall meant shutting down the entire warehouse for hours.
- G1 divides the warehouse floor into thousands of uniform modular storage pallets (Regions).
- G1 continuously monitors the trash density on every single pallet. When a scheduled cleaning break arrives, rather than cleaning the entire warehouse, the crew targets the specific pallets containing the most trash first (“Garbage-First”), evacuating valid shipments to clean pallets and freeing the dirtiest pallets within the allotted 15-minute break.
The “Garbage-First” Philosophy
Unlike legacy collectors that enforce physically contiguous boundaries for Young and Old generations, G1 dynamically divides the JVM Heap into approximately 2,048 equal-sized, logical Regions (ranging from 1 MB to 32 MB, always a power of 2).
┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
│ E │ O │ F │ E │ S │ O │ H │ F │
├──────┼──────┼──────┼──────┼──────┼──────┼──────┼──────┤
│ O │ E │ S │ F │ O │ F │ E │ H │
└──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┘
E = Eden Region S = Survivor Region O = Old Gen Region
H = Humongous Region F = Free Region (Awaiting allocation)
- Dynamic Role Assignment: Regions are not physically tied to specific generations. A region that serves as Eden during cycle 1 can be wiped clean and repurposed as an Old Generation or Survivor region during cycle 2.
- Predictable Pause Target: Developers configure a soft real-time latency target via
-XX:MaxGCPauseMillis=200(default 200 ms). G1 dynamically sizes collection work to stay within this pause budget.
🟡 Middle Level
The Core Internal Data Structures: CSet vs. RSet
A classic senior interview question evaluates the distinction between Collection Sets and Remembered Sets:
┌────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────┐
│ Collection Set (CSet) │ Remembered Set (RSet) │
├────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────┤
│ • "What regions am I collecting right now?" │ • "Which external regions contain pointers to ME?" │
│ • The set of memory regions selected to be evacuated │ • Maintained per region as a localized card table. │
│ during the current Stop-The-World (STW) pause. │ • Enables G1 to evacuate an individual region without │
│ • Young GC: CSet = All Eden + Survivor regions. │ scanning the entire multi-gigabyte heap! │
│ • Mixed GC: CSet = All Young + Highest-Yield Old Regs. │ • Updated asynchronously via JIT Write Barriers. │
└────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────┘
G1 Collection Lifecycle
G1 alternates between two primary collection modes supported by background marking:
1. Young GC (STW)
- Evacuates all Eden and Survivor regions into clean Survivor/Old regions.
- Updates RSet tracking data.
│
▼ (Old Gen occupancy surpasses IHOP threshold, default 45%)
2. Concurrent Marking Cycle
- Initial Mark (STW): Piggybacked on a Young GC; marks GC Roots.
- Concurrent Mark: Background threads trace live graph using SATB.
- Remark (STW): Drains pending SATB buffers; clears 100% empty regions.
- Cleanup (STW): Recovers completely dead regions; updates RSet metadata.
│
▼
3. Mixed GC Phase (STW, executed in ~8 incremental batches)
- Evacuates all Young regions alongside a selected batch of the
highest-garbage Old Generation regions ("Garbage-First").
🔴 Senior Level
Evacuation Failure (to-space exhausted)
During a Minor or Mixed GC pause, G1 evacuates live objects from regions in the CSet by copying them into available Free Regions.
The Catastrophe:
- If the application’s allocation rate is too high, or if Survivor/Old Gen regions are exhausted, G1 runs out of Free regions to receive surviving objects.
- The collector reports Evacuation Failure (
to-space exhausted). - Consequences: G1 must abort the evacuation, unroll partially moved references, self-heal pointers, and trigger an emergency fallback: a multi-threaded Stop-The-World Full GC.
- In modern Java (Java 10+), Full GC in G1 is parallelized (
Parallel Full GC), but it still halts all application threads for several seconds, violating latency SLAs.
Production Fixes:
- Increase total heap headroom (
-Xmx). - Increase
-XX:G1ReservePercent=15(default 10%), expanding the emergency buffer of free regions. - Lower the concurrent marking threshold:
-XX:InitiatingHeapOccupancyPercent=35to start background marking earlier.
Humongous Objects & Region Fragmentation
Any single object or array whose memory footprint exceeds 50% of a G1 region size is classified as a Humongous Object:
- Allocation: Bypasses TLAB and Young Generation entirely; allocated directly into a contiguous sequence of Old Generation regions.
- The Hazard: Humongous objects are never moved or compacted during Minor or Mixed GCs (they are reclaimed only during Cleanup or Full GC). If an application creates numerous short-lived byte arrays of several megabytes each, it rapidly fragments the free region pool. If a contiguous block of free regions cannot be found, G1 triggers a catastrophic Full GC.
- Tuning Fix: Increase region size to ensure large objects remain under the 50% threshold:
-XX:G1HeapRegionSize=16m # (or 32m, always a power of 2)
Transparent String Deduplication (-XX:+UseStringDeduplication)
Enterprise web applications often accumulate millions of duplicate String instances (e.g., identical HTTP headers, country codes, JSON keys).
By passing -XX:+UseStringDeduplication (introduced in Java 8u20):
- During region evacuation, G1 inspects candidate strings that have survived a few Minor GCs.
- If two distinct
Stringobjects share the exact same character sequence, G1 modifies the second string’s internalbyte[] valuereference to point to the first string’s array. - The duplicate array is reclaimed, typically slashing heap occupancy by 10%–25% with near-zero runtime CPU overhead.
Production Tuning Blueprint for G1 GC
# Set soft pause-time target (default 200ms; avoid setting below 50ms)
-XX:MaxGCPauseMillis=200
# Enable Adaptive IHOP to dynamically predict concurrent marking start
-XX:+G1UseAdaptiveIHOP
# Adjust region size to handle large byte/char arrays (>50% region size)
-XX:G1HeapRegionSize=16m
# Enable automatic String Deduplication for memory savings
-XX:+UseStringDeduplication
# Recommended unified GC logging format (Java 9+)
-Xlog:gc*,gc+phases=debug:file=/var/log/gc.log:time,uptime,pid:filecount=5,filesize=100M
[!CAUTION] The Golden Tuning Rule of G1: NEVER specify
-Xmnor-XX:NewRatio! Hardcoding Young Generation size overrides G1’s adaptive sizing algorithms. G1 can no longer contract Young Gen during traffic spikes, leading to severe pause-time SLA violations.
4 Tricky Questions
1. Why is the collector named “Garbage-First”, and how does it determine which regions to collect?
Answer: The name “Garbage-First” reflects G1’s core heuristic: prioritizing the collection of regions that contain the highest percentage of dead data. During the concurrent marking phase, G1 calculates the exact liveness and garbage density of every Old Generation region in the heap. When scheduling subsequent Mixed GC pauses, G1 sorts Old regions by garbage density and builds a Collection Set (CSet) starting with the “dirtiest” regions first. This maximizes the volume of reclaimed heap space per millisecond of pause time, ensuring the collector operates at peak efficiency within the configured -XX:MaxGCPauseMillis budget.
2. What is the fundamental difference between a Collection Set (CSet) and a Remembered Set (RSet)?
Answer: A Collection Set (CSet) represents the dynamic list of regions selected to be evacuated and reclaimed during the current GC pause (in a Young GC, it contains all Eden and Survivor regions; in a Mixed GC, it includes all Young regions plus a selected subset of high-yield Old regions). A Remembered Set (RSet) is a permanent, per-region card table structure that tracks which external regions hold references pointing into objects within this region. The RSet is what gives G1 its region-independence: during a collection pause, G1 scans only the RSets of the regions in the CSet to discover external pointers, avoiding the need to scan the rest of the heap.
3. What causes an Evacuation Failure (to-space exhausted) in G1 GC, and what are its performance consequences?
Answer: Evacuation Failure occurs when G1 is evacuating surviving objects from regions in the Collection Set, but the pool of clean Free Regions is completely exhausted. Because G1 cannot find a destination region to copy surviving objects into, it is forced to abort the evacuation. It unrolls partially updated object pointers, locks the surviving objects in their original regions, and triggers an emergency Stop-The-World Full GC to sweep and slide-compact the entire heap. This halts application mutator threads for several seconds, producing severe latency spikes and violating application SLAs.
4. Why is hardcoding the Young Generation size via -Xmn considered a critical anti-pattern when using G1 GC?
Answer: G1’s primary design goal is meeting a user-defined pause target (-XX:MaxGCPauseMillis, default 200 ms). To achieve this, G1 dynamically adjusts the Young Generation size between a lower bound (-XX:G1NewSizePercent, default 5%) and an upper bound (-XX:G1MaxNewSizePercent, default 60%) based on historical evacuation speeds. If allocation bursts cause evacuation pauses to exceed the target, G1 contracts the Young Gen in subsequent cycles to reduce the volume of data that needs evacuation. Hardcoding -Xmn freezes the Young Gen size to a rigid constant, completely disabling G1’s dynamic pause-time adjustment heuristics and causing severe, uncontrollable pause-time overshoots.
🎯 Interview Cheat Sheet
30-Second Elevator Pitch
“G1 GC (Garbage-First) is the default server-class collector since Java 9, engineered for multi-gigabyte heaps with predictable pause time targets (
-XX:MaxGCPauseMillis=200). It partitions the heap into ~2,048 modular Regions (1–32 MB). To collect regions independently without whole-heap scanning, G1 maintains per-region Remembered Sets (RSets). Old Generation space is reclaimed incrementally alongside Young Gen regions during Mixed GCs, targeting the regions with the highest garbage density first (Garbage-First). In production, never hardcode-Xmn, ensure large arrays do not cause Humongous Allocations, and enable-XX:+UseStringDeduplicationto save 10–25% heap memory.”
Key Architectural Takeaways
- Dynamic Regions: Heap is sliced into ~2,048 regions (1–32 MB); regions dynamically change roles (Eden, Survivor, Old, Humongous, Free).
- CSet vs RSet: CSet = regions being evacuated in current pause; RSet = per-region card table tracking incoming pointers from other regions.
- Mixed GC: Evacuates all Young regions + the highest-yield Old regions across incremental cycles to satisfy pause targets.
- Adaptive IHOP: Dynamically initiates background concurrent marking based on allocation speed and marking history.
- To-Space Exhausted: Evacuation failure when free regions run out, triggering a fallback multi-second Full GC.
- String Deduplication:
-XX:+UseStringDeduplicationconsolidates duplicate string byte arrays during GC evacuation.
Red Flags & Anti-Patterns
- 🚩 Setting
-Xmnor-XX:NewRatiowith G1 GC: Disables G1’s adaptive pause-time heuristic, leading to severe latency spikes. - 🚩 Configuring an unrealistic pause target like
-XX:MaxGCPauseMillis=5: Forces Young Gen down to an absurdly small size, causing hyper-frequent GCs and throughput collapse. - 🚩 Confusing CSet with RSet: CSet is what you are collecting; RSet is who points into your region.
- 🚩 Ignoring Humongous Object allocations in GC logs: Large allocations ($>50\%$ of region size) bypass Young Gen and cause Old Gen fragmentation.
- 🚩 Believing G1 never suffers from Full GCs: Evacuation failure or concurrent marking falling behind allocation will trigger an emergency STW Full GC.
Related Topics
- What are generations in GC — Heap vs Off-Heap generational architecture
- What is Old Generation (Tenured) — Mixed GC and Promotion Failure
- What GC algorithms exist — Algorithmic strategies and collector taxonomy
- What is ZGC — Sub-millisecond pause collector comparison
- What is stop-the-world — Safepoints and pause-time impact during Full GCs