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:

//是否应该并行 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

image

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpzdyd.html