1. 程式人生 > >2021-2-28:呼叫 System.gc() 後究竟發生了什麼?

2021-2-28:呼叫 System.gc() 後究竟發生了什麼?

首先,根據 DisableExplicitGC 這個 JVM 啟動引數的狀態,確定是否會 GC,如果需要 GC,不同 GC 會有不同的處理。 ### 1. G1 GC 的處理 如果是 `System.gc()` 觸發的 GC,G1 GC 會根據 ExplicitGCInvokesConcurrent 這個 JVM 引數決定是預設 GC (輕量 GC,YoungGC)還是 FullGC。 參考程式碼[`g1CollectedHeap.cpp`](https://github.com/openjdk/jdk/blob/jdk-17+11/src/hotspot/share/gc/g1/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`](https://github.com/openjdk/jdk/blob/jdk-17+11/src/hotspot/share/gc/z/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`](https://github.com/openjdk/jdk/blob/jdk-17+11/src/hotspot/share/gc/shenandoah/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**: >![image](https://zhxhash-blog.oss-cn-beijing.aliyuncs.com/qr-c