Garbage First(G1) GC 下篇 相关算法

上一篇介绍了G1 GC的使用场景,数据结构和GC过程,这一片我们来简单谈谈G1 GC过程中使用到的算法

1. SATB

全称是Snapshot-At-The-Beginning,是维持并发GC的一种手段,GC开始时活着的对象的一个快照,形成一个对象图,快照是通过Root Tracing得到的。此时活的对象就认为是活的,新生代的对象也认为是活的对象,除此之外其他不可达的对象都认为是垃圾对象。,用是维持并发GC的正确性,G1并发的基础就是SATB。 

那么它是怎么维持并发GC的正确性的呢?根据三色标记算法,我们知道对象存在三种状态:

  • 白:对象没有被标记到,标记阶段结束后,会被当做垃圾回收掉。

  • 灰:对象被标记了,但是它的field还没有被标记或标记完。

  • 黑:对象被标记了,且它的所有field也被标记完了。

由于并发阶段的存在,Mutator和Garbage Collector线程同时对对象进行修改,就会出现白对象漏标的情况,这种情况发生的前提是:

  • Mutator赋予一个黑对象该白对象的引用。

  • Mutator删除了所有从灰对象到该白对象的直接或者间接引用。

对于第一个条件,在并发标记阶段,如果该白对象是new出来的,并没有被灰对象持有,那么它会不会被漏标呢?Region中有两个top-at-mark-start(TAMS)指针,分别为prevTAMS和nextTAMS。在TAMS以上的对象是新分配的,这是一种隐式的标记。对于在GC时已经存在的白对象,如果它是活着的,它必然会被另一个对象引用,即条件二中的灰对象。如果灰对象到白对象的直接引用或者间接引用被替换了,或者删除了,白对象就会被漏标,从而导致被回收掉,这是非常严重的错误,所以SATB破坏了第二个条件。也就是说,一个对象的引用被替换时,可以通过write barrier 将旧引用记录下来。Write Barrier就是对引用字段进行赋值做了环切。通过Write Barrier就可以了解到哪些引用对象发生了什么样的变化。

2. 停顿预测模型

Pause Prediction Model ,停顿预测模型

G1 GC是一个响应时间优先的GC算法,它与CMS最大的不同是,用户可以设定整个GC过程的期望停顿时间,参数-XX:MaxGCPauseMillis指定一个G1收集过程目标停顿时间,默认值200ms,不过它不是硬性条件,只是期望值。

G1根据这个模型统计计算出来的历史数据来预测本次收集需要选择的Region数量,并根据用户配置的停顿时间来选择CSet的大小,从而尽量满足用户设定的目标停顿时间。

通过-XX:MaxGCPauseMillis参数来设置。这一点有点类似于ParallelScavenge收集器。关于停顿时间的设置并不是越短越好。设置的时间越短意味着每次收集的CSet越小,导致垃圾逐步积累变多,最终不得不退化成Serial GC;停顿时间设置的过长,那么会导致每次都会产生长时间的停顿,影响了程序对外的响应时间。

停顿预测模型

PPM模型是以衰减标准偏差为理论基础实现的:

// share/vm/gc_implementation/g1/g1CollectorPolicy.hpp

double get_new_prediction(TruncatedSeq* seq) {

  return MAX2(seq->davg() + sigma() * seq->dsd(),

  seq->davg() * confidence_factor(seq->num()));

}

在这个预测计算公式中:davg表示衰减均值,sigma()返回一个系数,表示信赖度,dsd表示衰减标准偏差,confidence_factor表示可信度相关系数。而方法的参数TruncateSeq,顾名思义,是一个截断的序列,它只跟踪了序列中的最新的n个元素。

在G1 GC过程中,每个可测量的步骤花费的时间都会记录到TruncateSeq(继承了AbsSeq)中,用来计算衰减均值、衰减变量,衰减标准偏差等:

// src/share/vm/utilities/numberSeq.cpp

void AbsSeq::add(double val) {

  if (_num == 0) {

    // if the sequence is empty, the davg is the same as the value

    _davg = val;

    // and the variance is 0

    _dvariance = 0.0;

  } else {

    // otherwise, calculate both

    _davg = (1.0 - _alpha) * val + _alpha * _davg;

    double diff = val - _davg;

    _dvariance = (1.0 - _alpha) * diff * diff + _alpha * _dvariance;

  }

}

比如要预测一次GC过程中,RSet的更新时间,这个操作主要是将Dirty Card加入到RSet中,具体原理参考前面的RSet。每个Dirty Card的时间花费通过_cost_per_card_ms_seq来记录,具体预测代码如下:

// share/vm/gc_implementation/g1/g1CollectorPolicy.hpp

double predict_rs_update_time_ms(size_t pending_cards) {

  return (double) pending_cards * predict_cost_per_card_ms();

}

double predict_cost_per_card_ms() {

  return get_new_prediction(_cost_per_card_ms_seq);

}

get_new_prediction就是我们开头说的方法,现在大家应该基本明白停顿预测模型的实现原理了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值