首先,根據 DisableExplicitGC 這個 JVM 啟動引數的狀態,確定是否會 GC,如果需要 GC,不同 GC 會有不同的處理。
1. G1 GC 的處理
如果是 System.gc()
觸發的 GC,G1 GC 會根據 ExplicitGCInvokesConcurrent 這個 JVM 引數決定是預設 GC (輕量 GC,YoungGC)還是 FullGC。
參考程式碼g1CollectedHeap.cpp
:
//是否應該並行 GC,也就是較為輕量的 GC,對於 GCCause::_java_lang_system_gc,這裡就是判斷 ExplicitGCInvokesConcurrent 這個 JVM 是否為 true
if (should_do_concurrent_full_gc(cause)) {
return try_collect_concurrently(cause,
gc_count_before,
old_marking_started_before);
}// 省略其他這裡我們不關心的判斷分支
else {
//否則進入 full GC
VM_G1CollectFull op(gc_count_before, full_gc_count_before, cause);
VMThread::execute(&op);
return op.gc_succeeded();
}
2. ZGC 的處理
直接不處理,不支援通過 System.gc()
觸發 GC。
參考原始碼:zDriver.cpp
void ZDriver::collect(GCCause::Cause cause) {
switch (cause) {
//注意這裡的 _wb 開頭的 GC 原因,這代表是 WhiteBox 觸發的,後面我們會用到,這裡先記一下
case GCCause::_wb_young_gc:
case GCCause::_wb_conc_mark:
case GCCause::_wb_full_gc:
case GCCause::_dcmd_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_scavenge_alot:
case GCCause::_jvmti_force_gc:
case GCCause::_metadata_GC_clear_soft_refs:
// Start synchronous GC
_gc_cycle_port.send_sync(cause);
break;
case GCCause::_z_timer:
case GCCause::_z_warmup:
case GCCause::_z_allocation_rate:
case GCCause::_z_allocation_stall:
case GCCause::_z_proactive:
case GCCause::_z_high_usage:
case GCCause::_metadata_GC_threshold:
// Start asynchronous GC
_gc_cycle_port.send_async(cause);
break;
case GCCause::_gc_locker:
// Restart VM operation previously blocked by the GC locker
_gc_locker_port.signal();
break;
case GCCause::_wb_breakpoint:
ZBreakpoint::start_gc();
_gc_cycle_port.send_async(cause);
break;
//對於其他原因,不觸發GC,GCCause::_java_lang_system_gc 會走到這裡
default:
// Other causes not supported
fatal("Unsupported GC cause (%s)", GCCause::to_string(cause));
break;
}
}
3. Shenandoah GC 的處理
Shenandoah 的處理和 G1 GC 的類似,先判斷是不是使用者明確觸發的 GC,然後通過 DisableExplicitGC 這個 JVM 引數判斷是否可以 GC(其實這個是多餘的,可以去掉,因為外層JVM_ENTRY_NO_ENV(void, JVM_GC(void))
已經處理這個狀態位了)。如果可以,則請求 GC,阻塞等待 GC 請求被處理。然後根據 ExplicitGCInvokesConcurrent 這個 JVM 引數決定是預設 GC (輕量並行 GC,YoungGC)還是 FullGC。
參考原始碼shenandoahControlThread.cpp
void ShenandoahControlThread::request_gc(GCCause::Cause cause) {
assert(GCCause::is_user_requested_gc(cause) ||
GCCause::is_serviceability_requested_gc(cause) ||
cause == GCCause::_metadata_GC_clear_soft_refs ||
cause == GCCause::_full_gc_alot ||
cause == GCCause::_wb_full_gc ||
cause == GCCause::_scavenge_alot,
"only requested GCs here");
//如果是顯式GC(即如果是GCCause::_java_lang_system_gc,GCCause::_dcmd_gc_run,GCCause::_jvmti_force_gc,GCCause::_heap_inspection,GCCause::_heap_dump中的任何一個)
if (is_explicit_gc(cause)) {
//如果沒有關閉顯式GC,也就是 DisableExplicitGC 為 false
if (!DisableExplicitGC) {
//請求 GC
handle_requested_gc(cause);
}
} else {
handle_requested_gc(cause);
}
}
請求 GC 的程式碼流程是:
void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) {
MonitorLocker ml(&_gc_waiters_lock);
//獲取當前全域性 GC id
size_t current_gc_id = get_gc_id();
//因為要進行 GC ,所以將id + 1
size_t required_gc_id = current_gc_id + 1;
//直到當前全域性 GC id + 1 為止,代表 GC 執行了
while (current_gc_id < required_gc_id) {
//設定 gc 狀態位,會有其他執行緒掃描執行 gc
_gc_requested.set();
//記錄 gc 原因,根據不同原因有不同的處理策略,我們這裡是 GCCause::_java_lang_system_gc
_requested_gc_cause = cause;
//等待 gc 鎖物件 notify,代表 gc 被執行並完成
ml.wait();
current_gc_id = get_gc_id();
}
}
對於GCCause::_java_lang_system_gc
,GC 的執行流程大概是:
bool explicit_gc_requested = _gc_requested.is_set() && is_explicit_gc(_requested_gc_cause);
//省略一些程式碼
else if (explicit_gc_requested) {
cause = _requested_gc_cause;
log_info(gc)("Trigger: Explicit GC request (%s)", GCCause::to_string(cause));
heuristics->record_requested_gc();
// 如果 JVM 引數 ExplicitGCInvokesConcurrent 為 true,則走預設輕量 GC
if (ExplicitGCInvokesConcurrent) {
policy->record_explicit_to_concurrent();
mode = default_mode;
// Unload and clean up everything
heap->set_unload_classes(heuristics->can_unload_classes());
} else {
//否則,執行 FullGC
policy->record_explicit_to_full();
mode = stw_full;
}
}
微信搜尋“我的程式設計喵”關注公眾號,每日一刷,輕鬆提升技術,斬獲各種offer: