jvm(10) g1觸發(fā)gc方法-源碼

在initialize_heap_sizes中初始化了gc的線程,后續(xù)的gc以任務(wù)的形式提交
主要有三種:
1.觸發(fā)gc操作一般在分配內(nèi)存時(shí),空間不足可能要oom時(shí)才尋找合適的stw時(shí)機(jī)
(某面試官說(shuō)道:有人的地方就有江湖,有分配內(nèi)存的地方都可能oom)
2.system.gc()
3.定時(shí)任務(wù)G1PeriodicGCTask
想看gc方法找gcRoots什么的需要一個(gè)切入點(diǎn),定時(shí)任務(wù)作為一個(gè)保底的觸發(fā)機(jī)制,可以不斷嘗試觸發(fā)gc

先看簡(jiǎn)單的定時(shí)任務(wù)

void G1PeriodicGCTask::execute() {
  check_for_periodic_gc();
  // G1PeriodicGCInterval is a manageable flag and can be updated
  // during runtime. If no value is set, wait a second and run it
  // again to see if the value has been updated. Otherwise use the
  // real value provided.
  //這個(gè)意思就是如果后續(xù)運(yùn)行過(guò)程中進(jìn)行了參數(shù)調(diào)整,可以實(shí)時(shí)調(diào)整
  schedule(G1PeriodicGCInterval == 0 ? 1000 : G1PeriodicGCInterval);
}
void ConcurrentGCThread::run() {
  // Setup handle area
  set_active_handles(JNIHandleBlock::allocate_block());

  // Wait for initialization to complete
  wait_init_completed();

  run_service();

  // Signal thread has terminated
  MonitorLocker ml(Terminator_lock);
  Atomic::release_store(&_has_terminated, true);
  ml.notify_all();
}

定時(shí)任務(wù)提交給了G1ServiceThread,這個(gè)類繼承到最上面是一個(gè)javaThread,和java一樣看run方法就行,最后調(diào)用到了task中的execute,
在執(zhí)行check_for_periodic_gc()完成后設(shè)置下一次的定時(shí)任務(wù)

void G1PeriodicGCTask::check_for_periodic_gc() {
  // If disabled, just return.
  // 間隔設(shè)置成0后將不會(huì)執(zhí)行g(shù)c,直接開(kāi)啟下一秒的gc任務(wù)
  if (G1PeriodicGCInterval == 0) {
    return;
  }
  //try_collect就是檢查過(guò)程,真的要gc的話看里面
  log_debug(gc, periodic)("Checking for periodic GC.");
  if (should_start_periodic_gc()) {
    if (!G1CollectedHeap::heap()->try_collect(GCCause::_g1_periodic_collection)) {
      log_debug(gc, periodic)("GC request denied. Skipping.");
    }
  }
}

這里的GCCause::_g1_periodic_collection為25

bool G1CollectedHeap::try_collect(GCCause::Cause cause) {
  //堆鎖判斷是否是當(dāng)前線程的鎖
  assert_heap_not_locked();

  // Lock to get consistent set of values.
  uint gc_count_before;
  uint full_gc_count_before;
  uint old_marking_started_before;
  {
    MutexLocker ml(Heap_lock);
    gc_count_before = total_collections();
    full_gc_count_before = total_full_collections();
    old_marking_started_before = _old_marking_cycles_started;
  }
  //判斷是否觸發(fā)并發(fā)的full gc
  if (should_do_concurrent_full_gc(cause)) {
    return try_collect_concurrently(cause,
                                    gc_count_before,
                                    old_marking_started_before);
  } else if (GCLocker::should_discard(cause, gc_count_before)) {
  //判斷是否是初始化gc并且獲取的gc次數(shù)被修改過(guò)
    // Indicate failure to be consistent with VMOp failure due to
    // another collection slipping in after our gc_count but before
    // our request is processed.
    return false;
  } else if (cause == GCCause::_gc_locker || cause == GCCause::_wb_young_gc
             DEBUG_ONLY(|| cause == GCCause::_scavenge_alot)) {
  //白盒子測(cè)試或者初始化
    // Schedule a standard evacuation pause. We're setting word_size
    // to 0 which means that we are not requesting a post-GC allocation.
    VM_G1CollectForAllocation op(0,     /* word_size */
                                 gc_count_before,
                                 cause,
                                 policy()->max_pause_time_ms());
    VMThread::execute(&op);
    return op.gc_succeeded();
  } else {
    // Schedule a Full GC.
    //簡(jiǎn)單的full gc
    VM_G1CollectFull op(gc_count_before, full_gc_count_before, cause);
    VMThread::execute(&op);
    return op.gc_succeeded();
  }
}

gc有多少種cause可以查看GCCause.cpp

case _java_lang_system_gc:
  return "System.gc()";
case _full_gc_alot:
  return "FullGCAlot";
case _scavenge_alot:
  return "ScavengeAlot";
case _allocation_profiler:
  return "Allocation Profiler";
case _jvmti_force_gc:
  return "JvmtiEnv ForceGarbageCollection";
case _gc_locker:
  return "GCLocker Initiated GC";
case _heap_inspection:
  return "Heap Inspection Initiated GC";
case _heap_dump:
  return "Heap Dump Initiated GC";
//...省略
case _g1_inc_collection_pause:
  return "G1 Evacuation Pause";

通過(guò)這個(gè)參數(shù)可以統(tǒng)計(jì)出整個(gè)jvm已經(jīng)進(jìn)行的gc類型,比如jstat -gccause <pid> 100 (意思是每秒打印gc信息)
其中有一個(gè)字段LGCC對(duì)應(yīng)這個(gè)原因

C:\Users\tango>jstat -gccause 21136 1000
  S0     S1     E      O      M     CCS    YGC     YGCT     FGC    FGCT     CGC    CGCT       GCT    LGCC                 GCC
  0.00  97.40  10.99  64.32  99.07  95.76     54     0.921     0     0.000     6     0.013     0.934 G1 Evacuation Pause  No GC
  0.00  97.40  10.99  64.32  99.07  95.76     54     0.921     0     0.000     6     0.013     0.934 G1 Evacuation Pause  No GC
  0.00  97.40  10.99  64.32  99.07  95.76     54     0.921     0     0.000     6     0.013     0.934 G1 Evacuation Pause  No GC
  0.00  97.40  10.99  64.32  99.07  95.76     54     0.921     0     0.000     6     0.013     0.934 G1 Evacuation Pause  No GC

jvm內(nèi)存觸發(fā)

結(jié)合前面內(nèi)存分配的過(guò)程就能快速找到

tlab觸發(fā)##

開(kāi)啟tlab后,如果內(nèi)存不夠走tlab_slow分配會(huì)調(diào)用這里,注意這里需要分配的東西是tlab的空間,與后續(xù)heap直接分配的方法一樣但是對(duì)象不一樣

HeapWord* G1CollectedHeap::allocate_new_tlab(size_t min_size,
                                             size_t requested_size,
                                             size_t* actual_size) {
//safepoint就是安全點(diǎn),jvm線程執(zhí)行字節(jié)碼自己能夠知道
  assert_heap_not_locked_and_not_at_safepoint();
//大對(duì)象直接走的heap,tlab不走
  assert(!is_humongous(requested_size), "we do not allow humongous TLABs");

  return attempt_allocation(min_size, requested_size, actual_size);
}

attempt_allocation和heap觸發(fā)的一樣

heap觸發(fā)##

HeapWord* MemAllocator::allocate_outside_tlab(Allocation& allocation) const {
  allocation._allocated_outside_tlab = true;
  HeapWord* mem = Universe::heap()->mem_allocate(_word_size, &allocation._overhead_limit_exceeded);
  if (mem == NULL) {
    return mem;
  }

  NOT_PRODUCT(Universe::heap()->check_for_non_bad_heap_word_value(mem, _word_size));
  size_t size_in_bytes = _word_size * HeapWordSize;
  _thread->incr_allocated_bytes(size_in_bytes);

  return mem;
}

mem_allocate

HeapWord*
G1CollectedHeap::mem_allocate(size_t word_size,
                              bool*  gc_overhead_limit_was_exceeded) {
  assert_heap_not_locked_and_not_at_safepoint();
 
  if (is_humongous(word_size)) {
    return attempt_allocation_humongous(word_size);
  }
  size_t dummy = 0;
//和tlab_slow一個(gè)方法
  return attempt_allocation(word_size, word_size, &dummy);
}

attempt_allocation_humongous
里面可以觸發(fā)gc原因的有
_g1_humongous_allocation
_g1_preventive_collection
特別注意大對(duì)象觸發(fā)時(shí),由于大對(duì)象消耗內(nèi)存很快,所以會(huì)觸發(fā)檢查是否要進(jìn)行并發(fā)標(biāo)記

// Humongous objects can exhaust the heap quickly, so we should check if we
  // need to start a marking cycle at each humongous object allocation. We do
  // the check before we do the actual allocation. The reason for doing it
  // before the allocation is that we avoid having to keep track of the newly
  // allocated memory while we do a GC.
  if (policy()->need_to_start_conc_mark("concurrent humongous allocation",
                                        word_size)) {
    collect(GCCause::_g1_humongous_allocation);
  }

attempt_allocation

inline HeapWord* G1CollectedHeap::attempt_allocation(size_t min_word_size,
                                                     size_t desired_word_size,
                                                     size_t* actual_word_size) {
  assert_heap_not_locked_and_not_at_safepoint();
  assert(!is_humongous(desired_word_size), "attempt_allocation() should not "
         "be called for humongous allocation requests");
//numa從系統(tǒng)的多線程空間獲取內(nèi)存
  HeapWord* result = _allocator->attempt_allocation(min_word_size, desired_word_size, actual_word_size);
//numa分配失敗
  if (result == NULL) {
    *actual_word_size = desired_word_size;
//內(nèi)部可能觸發(fā)gc再進(jìn)行內(nèi)存分配
    result = attempt_allocation_slow(desired_word_size);
  }

  assert_heap_not_locked();
  if (result != NULL) {
    assert(*actual_word_size != 0, "Actual size must have been set here");
    dirty_young_block(result, *actual_word_size);
  } else {
    *actual_word_size = 0;
  }

  return result;
}

system.gc()

JVM_ENTRY_NO_ENV(void, JVM_GC(void))
  if (!DisableExplicitGC) {
    EventSystemGC event;
    event.set_invokedConcurrent(ExplicitGCInvokesConcurrent);
    Universe::heap()->collect(GCCause::_java_lang_system_gc);
    event.commit();
  }
JVM_END
//collect
void G1CollectedHeap::collect(GCCause::Cause cause) {
  try_collect(cause);
}

回到了開(kāi)始的定時(shí)任務(wù),而且人家還是一秒一次的
如果版本支持G1PeriodicGCTask這個(gè)system.gc()就不要寫了吧

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容