ZGC

ZGC源码分析(4)-GC回收的10个步骤

2018-11-30  本文已影响0人  躺在石头上吐泡泡

前面我们提到过GC的启动时机。这一节中主要介绍GC的步骤。一个GC周期被分成10个小步骤,分别为:

注意些10步之间是串行执行的。

整体的活动图如下:

image.png

代码如下


void ZDriver::run_gc_cycle(GCCause::Cause cause) {
  ZDriverCycleScope scope(cause);

  // Phase 1: Pause Mark Start
  {
    ZMarkStartClosure cl;
    vm_operation(&cl);
  }

  // Phase 2: Concurrent Mark
  {
    ZStatTimer timer(ZPhaseConcurrentMark);
    ZHeap::heap()->mark();
  }

  // Phase 3: Pause Mark End
  {
    ZMarkEndClosure cl;
    while (!vm_operation(&cl)) {
      // Phase 3.5: Concurrent Mark Continue
      ZStatTimer timer(ZPhaseConcurrentMarkContinue);
      ZHeap::heap()->mark();
    }
  }

  // Phase 4: Concurrent Process Non-Strong References
  {
    ZStatTimer timer(ZPhaseConcurrentProcessNonStrongReferences);
    ZHeap::heap()->process_non_strong_references();
  }

  // Phase 5: Concurrent Reset Relocation Set
  {
    ZStatTimer timer(ZPhaseConcurrentResetRelocationSet);
    ZHeap::heap()->reset_relocation_set();
  }

  // Phase 6: Concurrent Destroy Detached Pages
  {
    ZStatTimer timer(ZPhaseConcurrentDestroyDetachedPages);
    ZHeap::heap()->destroy_detached_pages();
  }

  // Phase 7: Concurrent Select Relocation Set
  {
    ZStatTimer timer(ZPhaseConcurrentSelectRelocationSet);
    ZHeap::heap()->select_relocation_set();
  }

  // Phase 8: Concurrent Prepare Relocation Set
  {
    ZStatTimer timer(ZPhaseConcurrentPrepareRelocationSet);
    ZHeap::heap()->prepare_relocation_set();
  }

  // Phase 9: Pause Relocate Start
  {
    ZRelocateStartClosure cl;
    vm_operation(&cl);
  }

  // Phase 10: Concurrent Relocate
  {
    ZStatTimer timer(ZPhaseConcurrentRelocated);
    ZHeap::heap()->relocate();
  }
}

看到这里大家有没有疑问?似乎没有回收动作?实际上ZGC的回收在第6步中,也就是说转移完成后可回收的区域/Page是在下一次GC周期回收的。为什么这么设计?简单的说是为了效率,实际上ZGC中并没有一个明确的阶段回收Page,而是分散在不同的阶段中,这也就是意味着每一次Page回收之后立即就可以被再次使用。
还有一个小问题,就是C4这个论文里面明确的提到Remap,上图我们也看到。但上面的步骤中并没有看到。实际上ZGC中Remap这个阶段和第一步和第二步所覆盖。那么Remap到底是做什么的?简单的说,当被引用者对象发生了转移,需要更新引用者的指针,以便能准确的找到对象。ZGC中在每个Page中都设计了一个额外的数据结构用于保存对象转移前后的地址关系,所以当在对象被访问的时候通过这个映射关系可以找到对象新的位置。那么ZGC为什么不设计Remap阶段完成对象地址更新?原因其实也比较简单,在标记的时候会遍历所有的对象,在遍历的同时如果发现对象已经转移则直接更新,所以ZGC典型的是通过额外的内存来换取时间。

第一步初始标记

初始标记是需要STW,所以会通过VMThread的操作队列。通过辅助类Closure,关键代码是do_operation

class ZMarkStartClosure : public ZOperationClosure {
public:
  ...

  virtual bool do_operation() {
    ZStatTimer timer(ZPhasePauseMarkStart);
    ZServiceabilityMarkStartTracer tracer;

    // 是否启动软引用的收集,在三种情况需要回收
    // 内存已经不太足了
    // GC的启动是外部出发的,_wb_full_gc,_metadata_GC_clear_soft_refs
    const bool clear = should_clear_soft_references();
    ZHeap::heap()->set_soft_reference_policy(clear);

    // 设置boost模式,什么是boost模式呢?简单的说当ZGC发现内存几乎快被用光了,这个时候其实需要更多的GC线程工作加快回收速度
    // 这个boost模式为真的时候就说明需要加快,使用更多的GC线程。
    // 设置boost的后果就是根据并发线程数和并行线程数两者大的数字作为后续并发和并行的线程数
    // 在四种情况需要boost
    // 内存已经不太足了
    // GC的启动是外部出发的,_wb_full_gc,_java_lang_system_gc,_metadata_GC_clear_soft_refs
    const bool boost = should_boost_worker_threads();
    ZHeap::heap()->set_boost_worker_threads(boost);

    //这里表示进行的是Full GC,目前ZGC只有这一种方式
    ZCollectedHeap::heap()->increment_total_collections(true /* full */);
    //启动初始标记
    ZHeap::heap()->mark_start();
    return true;
  }
};
上一篇 下一篇

猜你喜欢

热点阅读