JVM G1 源码分析(七)- Full GC
程序员文章站
2022-05-06 17:52:02
...
1. 简介
当晋升失败、疏散失败、大对象分配失败、Evac失败时,有可能触发Full GC,在JDK10之前,Full GC是串行的,JEP 307: Parallel Full GC for G1之后引入了并行Full GC。本文主要介绍并行Full GC机制。
2. 源码分析
Full GC的入口在g1CollectedHeap.cpp的G1CollectedHeap::do_full_collection
bool G1CollectedHeap::do_full_collection(bool explicit_gc,
bool clear_all_soft_refs) {
assert_at_safepoint_on_vm_thread();
if (GCLocker::check_active_before_gc()) {
// Full GC was not completed.
return false;
}
const bool do_clear_all_soft_refs = clear_all_soft_refs ||
soft_ref_policy()->should_clear_all_soft_refs();
G1FullCollector collector(this, explicit_gc, do_clear_all_soft_refs);
GCTraceTime(Info, gc) tm("Pause Full", NULL, gc_cause(), true);
collector.prepare_collection();
collector.collect();
collector.complete_collection();
// Full collection was successfully completed.
return true;
}
- 准备回收,prepare_collection
- 回收,collect
- 回收后处理,complete_collection
2.1 准备阶段
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();
reference_processor()->enable_discovery();
reference_processor()->setup_policy(scope()->should_clear_soft_refs());
// When collecting the permanent generation Method*s may be moving,
// so we either have to flush all bcp data or convert it into bci.
CodeCache::gc_prologue();
// We should save the marks of the currently locked biased monitors.
// The marking doesn't preserve the marks of biased objects.
BiasedLocking::preserve_marks();
// Clear and activate derived pointer collection.
clear_and_activate_derived_pointers();
}
- Full GC应当清理软引用
- 由于Full GC过程中,永久代(元空间)中的方法可能被移动,需要保存bcp字节码指针数据或者转化为bci字节码索引
- 保存轻量级锁和重量级锁的对象头
- 清理和处理对象的派生关系
2.2 回收阶段
void G1FullCollector::collect() {
phase1_mark_live_objects();
verify_after_marking();
// Don't add any more derived pointers during later phases
deactivate_derived_pointers();
phase2_prepare_compaction();
phase3_adjust_pointers();
phase4_do_compaction();
}
- phase1 并行标记对象
- phase2 并行准备压缩
- phase3 并行调整指针
- phase4 并行压缩
2.2.1 并行标记
从GC roots出发,递归标记所有的活跃对象。
void G1FullCollector::phase1_mark_live_objects() {
// Recursively traverse all live objects and mark them.
GCTraceTime(Info, gc, phases) info("Phase 1: Mark live objects", scope()->timer());
// Do the actual marking.
G1FullGCMarkTask marking_task(this);
run_task(&marking_task);
// Process references discovered during marking.
G1FullGCReferenceProcessingExecutor reference_processing(this);
reference_processing.execute(scope()->timer(), scope()->tracer());
// Weak oops cleanup.
{
GCTraceTime(Debug, gc, phases) debug("Phase 1: Weak Processing", scope()->timer());
WeakProcessor::weak_oops_do(_heap->workers(), &_is_alive, &do_nothing_cl, 1);
}
// Class unloading and cleanup.
if (ClassUnloading) {
GCTraceTime(Debug, gc, phases) debug("Phase 1: Class Unloading and Cleanup", scope()->timer());
// Unload classes and purge the SystemDictionary.
bool purged_class = SystemDictionary::do_unloading(scope()->timer());
_heap->complete_cleaning(&_is_alive, purged_class);
} else {
GCTraceTime(Debug, gc, phases) debug("Phase 1: String and Symbol Tables Cleanup", scope()->timer());
// If no class unloading just clean out strings.
_heap->partial_cleaning(&_is_alive, true, G1StringDedup::is_enabled());
}
scope()->tracer()->report_object_count_after_gc(&_is_alive);
}
- 标记对象,具体逻辑在G1FullGCMarkTask中
- 清理弱引用
- 卸载类的元数据(complete_cleaning)或仅清理字符串(partial_cleaning)
- 清理字符串会清理StringTable和字符串去重(JEP 192: String Deduplication in G1)
G1FullGCMarkTask
void G1FullGCMarkTask::work(uint worker_id) {
Ticks start = Ticks::now();
ResourceMark rm;
G1FullGCMarker* marker = collector()->marker(worker_id);
MarkingCodeBlobClosure code_closure(marker->mark_closure(), !CodeBlobToOopClosure::FixRelocations);
if (ClassUnloading) {
_root_processor.process_strong_roots(
marker->mark_closure(),
marker->cld_closure(),
&code_closure);
} else {
_root_processor.process_all_roots_no_string_table(
marker->mark_closure(),
marker->cld_closure(),
&code_closure);
}
// Mark stack is populated, now process and drain it.
marker->complete_marking(collector()->oop_queue_set(), collector()->array_queue_set(), _terminator.terminator());
// This is the point where the entire marking should have completed.
assert(marker->oop_stack()->is_empty(), "Marking should have completed");
assert(marker->objarray_stack()->is_empty(), "Array marking should have completed");
log_task("Marking task", worker_id, start);
}
void G1RootProcessor::process_strong_roots(OopClosure* oops,
CLDClosure* clds,
CodeBlobClosure* blobs) {
StrongRootsClosures closures(oops, clds, blobs);
process_java_roots(&closures, NULL, 0);
process_vm_roots(&closures, NULL, 0);
_process_strong_tasks.all_tasks_completed(n_workers());
}
void G1RootProcessor::process_all_roots(OopClosure* oops,
CLDClosure* clds,
CodeBlobClosure* blobs,
bool process_string_table) {
AllRootsClosures closures(oops, clds);
process_java_roots(&closures, NULL, 0);
process_vm_roots(&closures, NULL, 0);
if (process_string_table) {
process_string_table_roots(&closures, NULL, 0);
}
process_code_cache_roots(blobs, NULL, 0);
_process_strong_tasks.all_tasks_completed(n_workers());
}
void G1RootProcessor::process_all_roots(OopClosure* oops,
CLDClosure* clds,
CodeBlobClosure* blobs) {
process_all_roots(oops, clds, blobs, true);
}
void G1RootProcessor::process_all_roots_no_string_table(OopClosure* oops,
CLDClosure* clds,
CodeBlobClosure* blobs) {
assert(!ClassUnloading, "Should only be used when class unloading is disabled");
process_all_roots(oops, clds, blobs, false);
}
- 如果允许卸载类的元数据,则调用process_strong_roots;否则调用process_all_roots_no_string_table
- process_strong_roots的GC roots仅强根
- process_all_roots_no_string_table的GC roots包括弱根、强根,但是不含StringTable
- 遍历标记栈中的所有对象
2.2.2 准备压缩
计算每个活跃对象应该在什么位置,即计算对象压缩后的新位置指针并写入对象头。
void G1FullCollector::phase2_prepare_compaction() {
GCTraceTime(Info, gc, phases) info("Phase 2: Prepare for compaction", scope()->timer());
G1FullGCPrepareTask task(this);
run_task(&task);
// To avoid OOM when there is memory left.
if (!task.has_freed_regions()) {
task.prepare_serial_compaction();
}
}
- 调用G1FullGCPrepareTask准备压缩
- 如果任务没有空闲Region,则调用prepare_serial_compaction串行合并所有线程的最后一个分区,以避免OOM
G1FullGCPrepareTask
void G1FullGCPrepareTask::work(uint worker_id) {
Ticks start = Ticks::now();
G1FullGCCompactionPoint* compaction_point = collector()->compaction_point(worker_id);
G1CalculatePointersClosure closure(collector()->mark_bitmap(), compaction_point);
G1CollectedHeap::heap()->heap_region_par_iterate_from_start(&closure, &_hrclaimer);
// Update humongous region sets
closure.update_sets();
compaction_point->update();
// Check if any regions was freed by this worker and store in task.
if (closure.freed_regions()) {
set_freed_regions();
}
log_task("Prepare compaction task", worker_id, start);
}
- 压缩对象具体逻辑在G1FullGCCompactionPoint中实现,执行完成后,对象头存储了对象的新地址
- 如果是大对象分区,且对象已经都死亡,则直接释放分区
2.2.3 调整指针
在上一步计算出所有活跃对象的新位置后,需要修改引用到新地址。
void G1FullCollector::phase3_adjust_pointers() {
// Adjust the pointers to reflect the new locations
GCTraceTime(Info, gc, phases) info("Phase 3: Adjust pointers", scope()->timer());
G1FullGCAdjustTask task(this);
run_task(&task);
}
void G1FullGCAdjustTask::work(uint worker_id) {
Ticks start = Ticks::now();
ResourceMark rm;
// Adjust preserved marks first since they are not balanced.
G1FullGCMarker* marker = collector()->marker(worker_id);
marker->preserved_stack()->adjust_during_full_gc();
// Adjust the weak roots.
if (Atomic::add(1u, &_references_done) == 1u) { // First incr claims task.
G1CollectedHeap::heap()->ref_processor_stw()->weak_oops_do(&_adjust);
}
AlwaysTrueClosure always_alive;
_weak_proc_task.work(worker_id, &always_alive, &_adjust);
CLDToOopClosure adjust_cld(&_adjust, ClassLoaderData::_claim_strong);
CodeBlobToOopClosure adjust_code(&_adjust, CodeBlobToOopClosure::FixRelocations);
_root_processor.process_all_roots(
&_adjust,
&adjust_cld,
&adjust_code);
// Adjust string dedup if enabled.
if (G1StringDedup::is_enabled()) {
G1StringDedup::parallel_unlink(&_adjust_string_dedup, worker_id);
}
// Now adjust pointers region by region
G1AdjustRegionClosure blk(collector()->mark_bitmap(), worker_id);
G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&blk, &_hrclaimer, worker_id);
log_task("Adjust task", worker_id, start);
}
- 调整之前保存的轻量级锁和重量级锁对象的引用地址
- 调整弱根
- 调整全部根对象
- 处理字符串去重逻辑
- 一个region一个region的调整引用地址
2.2.4 移动对象
对象的新地址和引用都已经更新,现在需要把对象移动到新位置
void G1FullCollector::phase4_do_compaction() {
// Compact the heap using the compaction queues created in phase 2.
GCTraceTime(Info, gc, phases) info("Phase 4: Compact heap", scope()->timer());
G1FullGCCompactTask task(this);
run_task(&task);
// Serial compact to avoid OOM when very few free regions.
if (serial_compaction_point()->has_regions()) {
task.serial_compaction();
}
}
- 具体压缩对象逻辑在G1FullGCCompactTask
- 如果phase2计算位置中使用了串行处理,则移动对象时也要使用串行处理移动每任务最后一个分区的对象
G1FullGCCompactTask
void G1FullGCCompactTask::work(uint worker_id) {
Ticks start = Ticks::now();
GrowableArray<HeapRegion*>* compaction_queue = collector()->compaction_point(worker_id)->regions();
for (GrowableArrayIterator<HeapRegion*> it = compaction_queue->begin();
it != compaction_queue->end();
++it) {
compact_region(*it);
}
G1ResetHumongousClosure hc(collector()->mark_bitmap());
G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&hc, &_claimer, worker_id);
log_task("Compaction task", worker_id, start);
}
size_t G1FullGCCompactTask::G1CompactRegionClosure::apply(oop obj) {
size_t size = obj->size();
HeapWord* destination = (HeapWord*)obj->forwardee();
if (destination == NULL) {
// Object not moving
return size;
}
// copy object and reinit its mark
HeapWord* obj_addr = (HeapWord*) obj;
assert(obj_addr != destination, "everything in this pass should be moving");
Copy::aligned_conjoint_words(obj_addr, destination, size);
oop(destination)->init_mark_raw();
assert(oop(destination)->klass() != NULL, "should have a class");
return size;
}
- 迭代处理每个Region
- 调用闭包G1CompactRegionClosure的apply函数移动对象到Region头部
- 如果Region中的全部对象都已清理,则回收该Region
3. 引用
jdk12源代码[https://hg.openjdk.java.net/jdk/jdk12]
JEP-307[http://openjdk.java.net/jeps/307]
JEP-192[http://openjdk.java.net/jeps/192]
上一篇: Java面试题及答案 (3)
下一篇: 整理 Java各类面试题(3)