Unlocking G1 FullGC: Inside OpenJDK’s Garbage‑First Full Collection Mechanism
This article dives deep into the OpenJDK G1 FullGC implementation, explaining its architecture, trigger conditions, preparation steps, and the four‑phase collection process while comparing single‑threaded and multi‑threaded implementations in JDK 8 and JDK 11, and highlighting code paths that can cause long GC pauses.
G1 FullGC Overview
The article explains the working principle of OpenJDK G1 (Garbage‑First) FullGC from the code level, starting at the GC entry point and analyzing core source code and execution flow. It compares single‑threaded and multi‑threaded implementations in OpenJDK 8 and OpenJDK 11, helping readers understand how object allocation patterns can affect GC duration.
G1 GC Basics
G1 divides the Java heap into fixed‑size regions (Eden, Survivor, Old, Humongous, Free). YoungGC collects only young regions, MixGC collects young plus selected old regions, and FullGC collects the entire heap when allocation fails or System.gc() is invoked. G1 offers parallel workers, concurrent marking, and region‑based generational collection, reducing pause times compared to CMS but with higher CPU load.
FullGC Trigger Conditions
FullGC is triggered when allocation fails after YoungGC/MixGC attempts, or when the application explicitly calls System.gc(). Both paths converge on do_collection() (no parallel variant) in JDK 8 and JDK 11.
<code>// Allocation failure entry
G1CollectedHeap::mem_allocate
VM_G1CollectForAllocation::doit()
G1CollectedHeap::satisfy_failed_allocation()
G1CollectedHeap::do_collection()
// System.gc() entry
G1CollectedHeap::collect(GCCause::Cause cause)
VM_G1CollectFull::doit()
G1CollectedHeap::do_full_collection()
G1CollectedHeap::do_collection()</code>Preparation Phase (prepare_collection)
In JDK 8 the preparation ends at G1MarkSweep::invoke_at_safepoint . JDK 11 encapsulates preparation, collection, and completion in the G1FullCollector class, making the logic clearer. Preparation includes data checks, concurrent‑mark handling, and heap‑wide cleanup.
<code>// JDK 11 preparation example
void G1FullCollector::prepare_collection() {
_heap->g1_policy()->record_full_collection_start();
_heap->print_heap_before_gc();
_heap->print_heap_regions();
_heap->abort_concurrent_cycle();
_heap->verify_before_full_collection(scope()->is_explicit_gc());
_heap->gc_prologue(true);
_heap->prepare_heap_for_full_collection();
// ... other init ...
}</code>Core Collection Logic (collect)
The FullGC process consists of four phases:
Phase 1 – Mark live objects
Phase 2 – Prepare compaction (calculate new addresses)
Phase 3 – Adjust pointers
Phase 4 – Perform compaction
<code>void G1FullCollector::collect() {
phase1_mark_live_objects();
phase2_prepare_compaction();
phase3_adjust_pointers();
phase4_do_compaction();
}</code>Each phase is implemented as a task derived from AbstractGangTask and executed by a pool of GangWorker threads. Workers are created based on -XX:ParallelGCThreads and run in a loop, waiting for tasks and processing them in parallel.
<code>// Worker loop
void GangWorker::run() {
initialize();
loop();
}
void GangWorker::loop() {
for (;;) {
WorkData data;
{
MutexLocker ml(gang()->monitor());
gang()->internal_worker_poll(&data);
}
if (data.task() != NULL) {
data.task()->work(part);
}
gang()->monitor()->wait(true);
}
}</code>Phase Details
Phase 1 – Mark Live Objects
Workers create a G1FullGCMarkTask that processes strong roots, marks live objects, and fills marking stacks. After marking, each worker calls marker->complete_marking() to finish.
<code>void G1FullGCMarkTask::work(uint worker_id) {
G1FullGCMarker* marker = collector()->marker(worker_id);
_root_processor.process_strong_roots(marker->mark_closure(),
marker->cld_closure(),
&code_closure);
marker->complete_marking(collector()->oop_queue_set(),
collector()->array_queue_set(),
&_terminator);
}</code>Phase 2 – Prepare Compaction
After marking, G1FullGCPrepareTask computes new object addresses using G1CalculatePointersClosure and iterates over live regions.
<code>void G1FullGCPrepareTask::work(uint worker_id) {
G1FullGCCompactionPoint* cp = collector()->compaction_point(worker_id);
G1CalculatePointersClosure closure(collector()->mark_bitmap(), cp);
G1CollectedHeap::heap()->heap_region_par_iterate_from_start(&closure, &_hrclaimer);
}</code>Phase 3 – Adjust Pointers
Workers run G1FullGCAdjustTask which processes all roots again, this time updating references to the new locations.
<code>void G1FullGCAdjustTask::work(uint worker_id) {
_root_processor.process_all_roots(&adjust, &adjust_cld, &adjust_code);
G1AdjustRegionClosure blk(collector()->mark_bitmap(), worker_id);
G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&blk, &_hrclaimer, worker_id);
}</code>Phase 4 – Compaction
Finally, G1FullGCCompactTask copies live objects to their new regions and clears old regions.
<code>void G1FullGCCompactTask::work(uint worker_id) {
for (auto* hr : *collector()->compaction_point(worker_id)->regions()) {
compact_region(hr);
}
// reset humongous regions and iterate remaining regions
}</code>Completion
After all phases, G1FullCollector::complete_collection() restores marks, updates biased locking, runs GC epilogues, and prepares the heap for mutators, rebuilding region lists and cleaning up metadata.
<code>void G1FullCollector::complete_collection() {
restore_marks();
BiasedLocking::restore_marks();
CodeCache::gc_epilogue();
JvmtiExport::gc_epilogue();
_heap->prepare_heap_for_mutators();
}</code>Conclusion
Compared with YoungGC and Concurrent Marking, G1 FullGC is a more comprehensive, independent process that touches many JVM subsystems, including object layout, region management, bitmap marking, root handling, and multithreaded coordination, offering valuable insights for performance tuning.
ByteDance SYS Tech
Focused on system technology, sharing cutting‑edge developments, innovation and practice, and analysis of industry tech hotspots.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.