Garbage-First (G1) is a server-oriented Java garbage collector designed to balance throughput with predictable pause times. Instead of treating the heap as a few fixed contiguous generations, G1 divides it into equal-sized regions and assigns roles such as Eden, Survivor, and Old dynamically.
Why regions matter
A region is a contiguous part of the Java heap. The JVM can choose a collection set made from regions that are expected to reclaim the most space for the available pause-time budget. This is the “garbage-first” idea.
Objects still follow a generational lifecycle, but generation sizes can adapt by changing how many regions have each role. Cross-region references are tracked using remembered sets so G1 does not need to scan the entire heap during every young collection.
Main collection phases
Young-only collections
New objects are allocated in Eden regions. During a stop-the-world young collection, live objects are copied to Survivor or Old regions. Copying compacts the selected regions as a natural part of evacuation.
Concurrent marking
When old-generation occupancy crosses an adaptive threshold, G1 begins a marking cycle. It identifies live objects across the old generation while application threads continue to run. Short stop-the-world phases start and finalize the cycle.
Mixed collections
After marking, G1 can include selected old regions with many reclaimable objects in subsequent young collections. These mixed collections gradually recover old-generation space without collecting every old region in one pause.
Full GC
A Full GC is a fallback and usually indicates evacuation failure, allocation pressure, insufficient headroom, or another condition that prevented normal concurrent progress. It should be investigated rather than treated as routine.
Humongous objects
Objects larger than roughly half a region are treated as humongous and occupy one or more contiguous regions. A high rate of large allocations can increase fragmentation and trigger earlier collections. GC logs and allocation profiling are more useful than changing region size blindly.
Basic JVM options
Modern server-class JDKs often choose G1 by default. Explicit options may include:
1 | -XX:+UseG1GC |
MaxGCPauseMillis is a goal, not a guarantee. Setting an unrealistically small value can force more frequent collections and reduce throughput.
What to measure before tuning
Start with evidence:
- pause duration percentiles and causes;
- allocation and promotion rates;
- heap occupancy before and after collection;
- concurrent-cycle start timing;
- humongous allocations;
- evacuation failures and Full GC events;
- CPU saturation and container memory limits.
Size the heap so the application has headroom for traffic bursts and concurrent marking. In containers, verify the JDK sees the intended CPU and memory limits.
Practical tuning order
- Use a supported, current JDK and collect GC logs.
- Confirm the heap is neither undersized nor competing with the operating system for memory.
- Reduce avoidable allocation pressure and oversized caches.
- Adjust the pause goal only after observing the throughput trade-off.
- Change advanced thresholds or region sizing only for a measured problem.
- Compare results under representative load, not a short synthetic run.
G1 tuning is workload-specific. A parameter set that helps one application can make another slower, so keep changes small and validate each one against latency, throughput, CPU, and memory metrics.
