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

首先,根據 DisableExplicitGC 這個 JVM 啓動參數的狀態,肯定是否會 GC,若是須要 GC,不一樣 GC 會有不一樣的處理。java

1. G1 GC 的處理

若是是 System.gc() 觸發的 GC,G1 GC 會根據 ExplicitGCInvokesConcurrent 這個 JVM 參數決定是默認 GC (輕量 GC,YoungGC)仍是 FullGC。git

參考代碼g1CollectedHeap.cppgithub

//是否應該並行 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)仍是 FullGCjvm

參考源碼shenandoahControlThread.cppasync

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 的代碼流程是:ide

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 的執行流程大概是:ui

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

相關文章
相關標籤/搜索