Hotspot CMS算法实现总结(四)

   目录

1、GenCollectedHeap::do_collection

2、CollectedHeap::collect_as_vm_thread

3、VM_CollectForMetadataAllocation

4、GenCollectedHeap::collect

5、mem_allocate_work

6、VM_GenCollectForAllocation

7、 Debug日志分析


    上一篇《Hotspot CMS算法实现总结 (二)》中总结了年轻代和老年代的GC主流程,本篇博客从年轻代和老年代的collect方法的调用方出发,来总结GC是如何被触发的。

1、GenCollectedHeap::do_collection

    do_collection方法是调用各Genaration的collect方法的直接上层方法,其主要流程如下:

其中计算start_level的逻辑如下:

DefNewGeneration采用父类Generation的默认实现,返回false,ConcurrentMarkSweepGeneration的实现如下,在默认配置下该方法返回true,即full为true,max_level为1的时候,start_level也为1,max_level是方法入参,表示执行GC的最大level。

//UseCMSCompactAtFullCollection默认为true,表示FullGC时所使用Mark-Sweep-Compact算法
//CollectGen0First默认为false,表示是否在FullGC前收集年轻代
 virtual bool full_collects_younger_generations() const {
    return UseCMSCompactAtFullCollection && !CollectGen0First;
  }

其中是否complete的判断逻辑如下:

 bool complete = full && (max_level == (n_gens()-1));
 int max_level_collected = starting_level;
 complete = complete || (max_level_collected == n_gens() - 1);

即只要max_level等于1的时候complete就为true,如果为1则为false。 

补充说明如下:

  1. 将Reference实例链表中剩余的Reference实例加入pending列表只适用于堆内存压缩式GC,正常的前台GC和后台GC都是在FinalMarking环节完成了该操作。
  2. 在各Genaration执行gc_epilogue时都会清理ChunkPool,在do_collection中还执行了一遍,主要是释放do_collection方法本身占用的ChunkPool内存。
  3. 通过老年代collect方法触发的堆内存压缩和前台GC都不会执行compute_new_size方法,该方法就是后台GC的Resizing步骤的核心,会根据当前占用的内存和配置的内存最大空闲率,内存最小空闲率来做必要的扩容和缩容。
  4. 如果start_level为0时,年轻代GC完成后会将size置为0,当进入老年代的should_collect方法时,因为size为0,该方法返回false,就不会触发老年代的GC。什么情况下start_level为0了?full为false时或者full为true,max_level为0时。
  5. 结合上面start_level的分析,可知max_level为1的时候,full为false时do_collection只调用年轻代GC,full为true时实际只调用老年代GC,当max_level为0的时候只调用年轻代的collect方法,虽然代码中是循环调用start_level到max_level之间的Genaration的collect方法,但是实际只调用一次。

 2、CollectedHeap::collect_as_vm_thread

      collect_as_vm_thread方法是给VMThread使用的执行GC的内部方法,该方法假定已经获取了Heap_lock锁,其实现如下:

void CollectedHeap::collect_as_vm_thread(GCCause::Cause cause) {
  //校验是VMThread
  assert(Thread::current()->is_VM_thread(), "Precondition#1");
  //校验获取了Heap_lock
  assert(Heap_lock->is_locked(), "Precondition#2");
  //临时设置GCCause
  GCCauseSetter gcs(this, cause);
  switch (cause) {
    case GCCause::_heap_inspection: //表示打印堆内存中包含的类的直方图
    case GCCause::_heap_dump: //堆内存dump
    case GCCause::_metadata_GC_threshold : { //表示元空间内存分配失败
      HandleMark hm;
      do_full_collection(false);        //false表示不需要清除软引用
      break;
    }
    case GCCause::_last_ditch_collection: { //执行过do_full_collection(false)后再次尝试从元空间分配内存,分配失败后就尝试扩容再分配,依然失败就会使用这个GCCause
      HandleMark hm;
      do_full_collection(true);         //true表示清除软引用
      break;
    }
    default:
      ShouldNotReachHere(); // Unexpected use of this function
  }
}

void GenCollectedHeap::do_full_collection(bool clear_all_soft_refs,
                                          int max_level) {
  int local_max_level;
  if (!incremental_collection_will_fail(false /* don't consult_young */) &&
      gc_cause() == GCCause::_gc_locker) {
    local_max_level = 0;
  } else {
    local_max_level = max_level;
  }

  do_collection(true                 /* full */,
                clear_all_soft_refs  /* clear_all_soft_refs */,
                0                    /* size */,
                false                /* is_tlab */,
                local_max_level      /* max_level */);
 
  if (local_max_level == 0 && gc_cause() == GCCause::_gc_locker &&
      incremental_collection_will_fail(false /* don't consult_young */)) {
    if (PrintGCDetails) {
      gclog_or_tty->print_cr("GC locker: Trying a full collection "
                             "because scavenge failed");
    }
    //如果老年代空间不足,则回收老年代
    do_collection(true                 /* full */,
                  clear_all_soft_refs  /* clear_all_soft_refs */,
                  0                    /* size */,
                  false                /* is_tlab */,
                  n_gens() - 1         /* max_level */);
  }
}

在collect_as_vm_thread场景下do_full_collection方法实际就只执行do_collection,其中full为true,max_level就是1,即只回收老年代。collect_as_vm_thread的调用链如下:

3、VM_CollectForMetadataAllocation

     VM_CollectForMetadataAllocation是元空间内存分配失败后触发GC的实现,参考其构造方法的调用链,如下:

 该类的核心doit方法的主流程如下:

补充说明如下:

  1. 上述步骤中每次尝试分配成功后就会立即返回,分配失败则继续走下一步。第一步尝试直接分配而不是触发GC是因为一旦元空间不足了,可能会触发多个线程执行VM_CollectForMetadataAllocation,但是VMThread一次只处理一个,即存在其他线程已经触发GC释放了足够的内存,所以这里再做一次尝试分配。
  2. 第二步判断时,只有CMS算法和G1算法在GC时会卸载Klass,这两种算法在默认配置下返回true,其他的返回false。以CMS的实现为例,在调用该方法判断时,会将MetaspaceGC的_sho
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值