半区复制算法

前言

半区复制算法的目的也是为了更好的缓解内存碎片问题。对比于标记-压缩算法, 它不需要遍历堆内存那么多次,节约了时间,但是它也带来了一个主要的缺点,那就是相比于标记-清除和标记-压缩垃圾回收器,它的可用堆内存减少了一半。同时对于大对象,复制比标记的代价更大。所以半区复制算法更一般适合回收小的,存活期短的对象。

三色抽象法

在我们深入半区复制算法原理前,我们需要了解下什么是三色抽象法。对于一个对象,垃圾收集器可以将其标记为灰色,黑色和白色中的一种,每种颜色代表不同的含义,

  1. 灰色 - 表示垃圾收集器已经访问过该对象,但是还没有访问过它的所有孩子节点。
  2. 黑色 - 表示该对象以及它的所有孩子节点都已经被垃圾收集器访问过了。
  3. 白色 - 表示该对象从来没有被垃圾收集器访问过,这就是非可达对象。

三色抽象法也可以用在标记-清除算法和标记-压缩算法。当垃圾收集结束后,可达对象都被标记为黑色,非可达对象都被标记为白色,不会有灰色对象存在。在半区复制算法里,我们也采用了三色抽象法来标记对象。

算法原理

之所以叫半区复制,是因为它将堆内存对半分为两个半区,只用其中一个半区来进行对象内存的分配,如果在这个半区内存不够给新的对象分配了,那么就开始进行垃圾收集,将这个半区中的所有可达对象都拷贝到另外一个半区中去,然后继续在另外那个半区进行新对象的内存分配。半区复制算法中比较常见是Cheney算法。

下面是复制算法的伪代码:

atomic collect():
    flip()
    scan <- free
    for each fld in Roots
        process(fld)
    while not isEmpty(worklist)
        ref <- remove(worklist)
        scan(ref)

flip():
    fromspace, tospace <- tospace, fromspace
    top <- tospace+extent  //界定堆内存的边界
    free <- tospace

scan(ref):
    for each fld in Pointers(ref)
        process(fld)

process(fld):
    fromRef <- *fld
    if fromRef != null
        *fld <- forward(fromRef) //将可达对象复制后的地址写到原对象的位置上,当作迁移地址
    
forward(fromRef):
    toRef <- forwardingAddress(fromRef) //读取该位置上对象的迁移地址
    if toRef == null //如果该位置上的对象没有迁移地址,那就说明它还没有被复制,需要复制到tospace中去
        toRef <- copy(fromRef)
    return toRef
    
copy(fromRef):
    toRef <- free 
    free <- free + size(fromRef)
    move(fromRef, toRef) //从fromRef位置复制对象到toRef位置上
    forwardingAddress(fromRef) <- toRef  //将地址toRef写到fromRef位置上的对象中去
    add(worklist, toRef)
    return toRef

remove(worklist):
    ref <- scan
    scan <- scan + size(scan)
    return ref

实际上半区复制算法的实现跟标记-压缩算法的实现差不多, 都是采用的深度遍历算法,理解该算法的关键点是,怎么计算可达对象的迁移地址(forwardingAddress) - 看copy(fromRef)方法的实现, 以及怎么更新对象间的引用关系。

半区复制算法的过程如下:


复制算法

复制算法

我们假设A,B对象是根对象。

  1. 首先先交换左右半区(ToSpace, FromSpace), 同时设置free指针和top指针。
  2. 遍历处理根对象A,B。先将A对象复制到free指针指向的位置,同时将A对象复制后的地址(迁移地址)写到原先A对象所在的位置,图中虚线的箭头表示。可以看到A对象已经被collector访问过了,但是还没有访问其孩子节点,所以将其标为了灰色。紧接着scan,free指针继续向前移动。
  3. 由于是深度遍历算法,紧接collector会先遍历处理A对象所引用的对象C,当发现对象C没有迁移地址时,说明它还没有被复制,由于它又是可达对象,所以接着collector会将它复制到当前free指针指向的位置,即对象A后面。对象C复制完后,会用其复制后的地址来更新A原先对C的引用,同时也写到原先C对象所在的地址上。
  4. 接着collector会处理对象C的孩子节点(深度遍历算法),由于对象C没有引用任何对象,于是对象C的处理结束,将其标记为黑色。然后collector接着处理A对象的另外一个孩子节点E对象,处理方式跟处理对象C一致。
  5. 对象E也没有孩子节点,collector也将其标识为黑色。
  6. 到目前为此,A对象也全部处理结束了,于是collector将其标识为黑色,然后接着去处理对象B。当复制B对象结束后,发现B对象所引用的对象C有迁移地址,于是就更新其对对象C的引用,使其指向FromSpace半区中对象C的迁移地址 - 即C对象复制后所在ToSpace的地址。这个情况下就不需要再次复制对象C了。
  7. 当所有的可达对象都从FromSpace半区复制到ToSpace半区后,垃圾收集结束。新对象的内存分配从free指针指向的位置开始进行分配。

其实,从垃圾收集过程中对象的移动顺序来看,collector将相邻的对象都复制在相近的位置上,它属于前面我们在标记-压缩算法里所说的“线性顺序”。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值