jvm(10) g1触发gc方法-源码

在initialize_heap_sizes中初始化了gc的线程,后续的gc以任务的形式提交
主要有三种:
1.触发gc操作一般在分配内存时,空间不足可能要oom时才寻找合适的stw时机
(某面试官说道:有人的地方就有江湖,有分配内存的地方都可能oom)
2.system.gc()
3.定时任务G1PeriodicGCTask
想看gc方法找gcRoots什么的需要一个切入点,定时任务作为一个保底的触发机制,可以不断尝试触发gc

先看简单的定时任务

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.
  //这个意思就是如果后续运行过程中进行了参数调整,可以实时调整
  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();
}

定时任务提交给了G1ServiceThread,这个类继承到最上面是一个javaThread,和java一样看run方法就行,最后调用到了task中的execute,
在执行check_for_periodic_gc()完成后设置下一次的定时任务

void G1PeriodicGCTask::check_for_periodic_gc() {
  // If disabled, just return.
  // 间隔设置成0后将不会执行gc,直接开启下一秒的gc任务
  if (G1PeriodicGCInterval == 0) {
    return;
  }
  //try_collect就是检查过程,真的要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) {
  //堆锁判断是否是当前线程的锁
  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;
  }
  //判断是否触发并发的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次数被修改过
    // 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)) {
  //白盒子测试或者初始化
    // 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.
    //简单的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";

通过这个参数可以统计出整个jvm已经进行的gc类型,比如jstat -gccause <pid> 100 (意思是每秒打印gc信息)
其中有一个字段LGCC对应这个原因

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内存触发

结合前面内存分配的过程就能快速找到

tlab触发##

开启tlab后,如果内存不够走tlab_slow分配会调用这里,注意这里需要分配的东西是tlab的空间,与后续heap直接分配的方法一样但是对象不一样

HeapWord* G1CollectedHeap::allocate_new_tlab(size_t min_size,
                                             size_t requested_size,
                                             size_t* actual_size) {
//safepoint就是安全点,jvm线程执行字节码自己能够知道
  assert_heap_not_locked_and_not_at_safepoint();
//大对象直接走的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触发的一样

heap触发##

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一个方法
  return attempt_allocation(word_size, word_size, &dummy);
}

attempt_allocation_humongous
里面可以触发gc原因的有
_g1_humongous_allocation
_g1_preventive_collection
特别注意大对象触发时,由于大对象消耗内存很快,所以会触发检查是否要进行并发标记

// 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从系统的多线程空间获取内存
  HeapWord* result = _allocator->attempt_allocation(min_word_size, desired_word_size, actual_word_size);
//numa分配失败
  if (result == NULL) {
    *actual_word_size = desired_word_size;
//内部可能触发gc再进行内存分配
    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);
}

回到了开始的定时任务,而且人家还是一秒一次的
如果版本支持G1PeriodicGCTask这个system.gc()就不要写了吧

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容