RC Immix

Rifat Shariyar等,Reference Counting Immix,2013

目的

RC Immix算法将引用计数的一大缺点(吞吐量低)改善到了实用的级别。本算法改善了引用计数算法的“合并型引用计数法”和Immix组合起来使用。

合并型引用计数

Yossi Levanoni, Erez Petrank,2001

之前我们说过,在吞吐量方面引用计数不如搜索型GC。其原因是引用计数器频繁增减。在引用计数中,每当对象之间的关系发生变化,对象的计数器就会发生变化。如果计数器频繁发生增减,那么写入屏障执行的频率就会增大,处理就会变得繁重。

  • 在这里注意下,如果我们队一个对象的计数器执行加后在执行减。因为两者会互相抵消,最终计数器并没有变化。由此可知可知,比起一直保持计数器的数值正确,不如计数器的增量和减量相互抵消这样更方便管理,节省资源。

  • 于是出现了一种新的方法,就是把注意力只放在初始和最后的状态上,该期间内不对计数器进行修改。这就是合并型引用计数法(Coalesced Reference Counting)。在该方法中,即使指针发生改动,计数器也不会增减。指针改动时的信息会被注册到更改缓冲区

  • 如果对象间的引用关系发生变化,就会导致计数器值是错误的。如果期间ZCT满了就要去查找ZCT并更正计数器的值。

  • 在合并型引用计数法中,要将指针发生改动的对象和其所有子对象注册到更改缓冲区中。这项操作是通过写入屏障来执行的。不过因为这个时候我们不更新计数器,所以计数器的值会保持错误。

  • 我们将指针改动了的X和指针改动前被X引用的A注册到缓冲区。因为没有更新计数器,所以A和B的计数器在这个时候是不正确的。
  • 等到缓冲区满了,就要运行GC了。合并型引用计数法中的GC指的是查找更改缓冲区。并正确设置计数器的过程。通过查找更改缓冲区,如何重新正确设定计数器的值,用下图来说明。

  • 首先,X将其指针从A变更到B。此时我们把X和其子对象A注册到缓冲区。
  • 然后假设X的元引用对象发送了B->A->B这样的变化。因为我们已经吧X注册到更改缓冲区了,所以没有必要进行重新注册。
  • 接下来,假设d阶段更改缓冲区满了,则是后就启动GC了。首先查找更改缓冲区,我们可以得到以下信息。
    • X在 某个阶段引用的是A
    • X现在引用的是B
    • 对A的计数器进行减量
    • 对B的计数器进行加量。

伪代码

# 合并型引用计数法的写入屏障
write_barrier_coalesced_RC(obj, field, dst){
if(!obj.dirty)
register(obj)
obj.field = dst }

写入屏障负责检查要改动的指针的对象obj的标识dirty是否注册完毕。如果没有注册,就将其注册到更改缓冲区($mod_buf)。执行注册的方法是register函数。

# register
register(obj){
if($mod_buf.size <= $mod_buf.used_sized)
garbage_collect() entry.obj = obj
foreach(child_ptr :children(obj))
if(*child_ptr != nil)
push(entry.children, *child_ptr) push($mod_buf, entry)
obj.dirty = true }
  • 首先,当$mod_buf满的时候,我们就要执行GC。
  • 接下来,准备obj的信息。以将其注册到更改缓冲区。这个entry是指向某对象所有子对象的指针集合。我们将这个信息作为$mod_buf的一个元素进行注册。
  • 最后设置的dirty标识已经将其注册过了。
#garbage_collect
garbage_collect(){
foreach(entry: $mod_buf)
obj = entry.obj
foreach(child:obj)
inc_ref_cnt(child)
foreach(child : entry.children)
dec_ref_cnt(child)
obj.dirty = false
clear($mod_buf) }
  • 合并型引用计数法是将某一时期最初的状态和最后的状态进行比较,合理调整计数器的算法。
  • 在garbage_collect()中,先查找$mod_buf,对于已经注册的对象进行如下处理。
    • 对obj现在的子对象的计数器进行增量
    • 对obj以前的子对象的计数器进行减量

      通过以上操作,根据对象最初的最后的状态对计数器进行合理的调整。此外先进行增量是为了确保AB是同一对象时也能后顺利运行。

优点和缺点

  • 优点:增加了吞吐量
  • 缺点:增加了mutator的暂停时间。

合并型引用计数法和Immix的融合

RC Immix中,对象不仅有计数器,线也有计数器,这样就可以获悉线内是否存在活动对象。不过线的计数器和对象有所不同。对象的计数器表示的是指向这个对象的指针数。而线的计数器则是表示这个线里存活的对象数量。如果变成了0就将整个线回收。

对象生成和废弃的频率要低于对象间引用关系变化的频率,这样一来 更新计数器所产生的额外负担就小了。


下面来说一下RC Immix中是如何以线为单位进行内存管理的。

dec_ref_cnt(obj){
obj.ref_cnt --
if(obj.ref_cnt ==0)
reclaim_obj(obj)
line = get_line(obj)
line.ref_cnt--
if(line.ref_cnt == 0 )
reclaim_line(line) }

当对象的计数器为0的时候,对线的计数器进行减量。当线的计数器为0 的时候,我们就可以回收整条线。

此方法虽然把合并型引用计数和Immix组合到了一起,但是不能执行压缩。

在压缩中要进行复制对象的操作。要实现这些操作,不仅要复制对象,还要将引用此对象的指针全部改写。因此压缩所需的信息这里是不能提供的。

于是RC Immix通过限定对象来时实现压缩。就是下面要说的新对象。

新对象

在RC Immix中,把没有经历过GC的对象称为新对象。即就是在上一次GC之后生成的对象。

  • 更改缓冲区里记录的是从上一次GC开始到现在为止指针改动过的对象。
  • 所有指向新对象的指针都是上一次GC之后生成的。也就是说,所有引用新对象的对象都被注册到了更改缓冲区。
  • 因此可以通过查找更改缓冲区,只对新对象进行复制操作。
  • 利用这条性质,RC Immix中以新对象为对象进行压缩,这种方法称为被动的碎片整理(Reactive Defragmentation)

被动的碎片整理

RC Immix,在更改缓冲区满了的时候会查找更改缓冲区,这时如果发现了新的对象,就把他复制到别的空间去。

我们准备一个空的块来当做目标空间。在复制过程中目标空间满了的情况下,就采用一个空的块。我们不对旧对象执行被动的碎片整理。RC Immix中的 garbage_collect()函数代码清单如下。

garbage_collect(){
dst_block = get_empty_block()
foreach(entry :$mod_but)
obj = entry.obj
foreach(child_ptr :children(obj))
inc_ref_cnt(*child_ptr)
if(!(*child_ptr).old)
reactive_defrag(child_ptr, dst_block)
foreach(child : entry.children)
dec_ref_cnt(child)
obj.dirty = false }

这里的garbage_collect()函数和合并型引用计数法中的garbage_collect()函数很像。基本流程都是查找更改缓冲区,根据情况增量或者减量操作。

不同的是这里对新对象调用了reactive_defrag()函数,这就是被懂得碎片整理。那么我们来看看 reactive_defrag()函数。

reactive_defrag(ptr, dst_block){
obj = *ptr
if(obj.copied)
*ptr = obj.forwarding
else
if(obj.size>dst_block.free_size)
dst_block = get_empty_block() new_obj = dst_block.free_top
copy_data(obj, new_obj, obj.size)
obj.forwarding = new_obj
*ptr = new_obj
obj.copied = true
new_obj.old = true
dst_block.free_top += obj.size
dst_block.free_size += obj.size
line = get_line(obj)
line.ref_cnt++ }
  • 复制对象并设定forwarding指针。在RC Immix中还需要留意线计数器。将对象复制到线时,也要对线的计数器进行增量。
  • 通过被动的碎片整理,就可以以引用计数法为基础,来执行压缩。
  • 此外,因为我们以引用计数法为基础,所以不能解决循环引用的问题。

    为了解决问题,可以使用积极地碎片整理(Proactive Defragmentation)

积极的碎片整理

被动的碎片整理的两处缺陷

  • 无法对旧对象进行压缩
  • 无法回收有循环引用的垃圾

为了解决这些问题,RC Immix中进行了被动的碎片处理之外,还进行了另一项操作。也就是积极的碎片整理

  • 首先决定要复制到那个块,然后把能够通过指针从根查找到的对象全都复制过去。
  • 通过积极的碎片整理,对就对象进行压缩和回收循环垃圾都成为了可能。
  • 他还有以个优点就是可以重置计数器。如果某个对象发生计数器溢出,通过执行积极的碎片整理,就会从根重新查找所有指针,也就能重新设定计数器的值。

优点和缺点

优点

  • 吞吐量得到改善,据说平均提高了12个点。甚至会超过搜索型GC。
  • 吞吐量改善的原因是因为合并型引用计数法没有通过写入屏障来执行计数器的增减操作。即使对象之间的引用关系频繁发生变化,吞吐量也不会下降太多。
  • 吞吐量改善的原因是撤出了空闲链表。通过以线为单位来管理分块,只要在线内移动指针就可以进行分配。此外省去了把分块重新连接到空闲链表上的操作。

缺点

  • 会增加暂停时间,不过可以通过调整缓冲区的大小缩短暂停时间。
  • 只要线内还有一个非垃圾对象,就无法将其回收。也就是说线内只要有一个活动对象就会浪费一条线。

RC Immix的更多相关文章

  1. 《垃圾回收的算法与实现》——增量式垃圾回收与RC Immix算法

    增量式垃圾回收 为了控制最大暂停时间,通过逐渐推进垃圾回收即垃圾回收与mutator交替执行. 三色标记算法 以标记-清除算法为例使用三色标记算法. 利用降低吞吐量来缩短最大停顿时间. 基础 将GC中 ...

  2. 深入研究Visual studio 2017 RC新特性

    在[Xamarin+Prism开发详解三:Visual studio 2017 RC初体验]中分享了Visual studio 2017RC的大致情况,同时也发现大家对新的Visual Studio很 ...

  3. Xamarin+Prism开发详解三:Visual studio 2017 RC初体验

    Visual studio 2017 RC出来一段时间了,最近有时间就想安装试试,随带分享一下安装使用体验. 1,卸载visual studio 2015 虽然可以同时安装visual studio ...

  4. Create an offline installation of Visual Studio 2017 RC

    Create an offline installation of Visual Studio 2017 RC ‎2016‎年‎12‎月‎7‎日                             ...

  5. Android中的 init.rc文件简介

    init.rc脚本是由Android中linux的第一个用户级进程init进行解析的. init.rc 文件并不是普通的配置文件,而是由一种被称为"Android初始化语言"(An ...

  6. TypeScript 2.0候选版(RC)已出,哪些新特性值得我们关注?

    注:本文提及到的代码示例下载地址 - Runnable sample to introduce Typescript 2.0 RC new features 作为一个Javascript的超集, Ty ...

  7. vs2017 rc 离线安装包制作

    vs2017 rc 离线安装包制作 1.下载在线安装包:https://aka.ms/vs/15/release/vs_Enterprise.exe 2.制作离线安装包: vs_Enterprise. ...

  8. 【Win10 应用开发】解决VS 2015 RC不能调试手机应用的问题

    VS2015 RC已发布,当然这个版本还不能用于实际生产中,如果你没有测试环境,就等正式版出来,RC都来了,RTM就不远了. 如果你也像老周一样,已经在耍RC版了,你可能会遇到下面问题: 安装Win ...

  9. 使用 python 实现 memcached 的启动服务脚本 rc

    #!/usr/bin/python #coding:utf-8 import sys import os from subprocess import Popen, PIPE class Memcac ...

随机推荐

  1. Resolving Problems installing the Java JCE Unlimited Strength Jurisdiction Policy Files package--转

    原文地址:https://www.ca.com/us/services-support/ca-support/ca-support-online/knowledge-base-articles.tec ...

  2. linux批处理笔记

    最近不得不用到Linux批处理,于是把要用到的程序反复研究了一下. #!/bin/bash是指此脚本使用/bin/bash来解释执行. -le -ge分别是小于和大于,这个倒是和latex里面的命令很 ...

  3. (转载)RecyclerView之ItemDecoration由浅入深

    RecyclerView之ItemDecoration由浅入深 作者 小武站台 关注 2016.09.19 18:20 字数 1155 阅读 10480评论 15喜欢 91赞赏 3 译文的GitHub ...

  4. [ Docker ] 映射資料夾

    - docker run -v <host path>:<container path> - 例如:docker run -v /home/adrian/data:/data ...

  5. iptables 简单介绍及应用 Linux防火墙

    iptables 即 Linux防火墙 的简单介绍及使用 iptables生效位置如下图: 其中, 网络防火墙也可以使用一台启用了iptables的Linux主机代替; 路由器或集线器等设施在拓扑中省 ...

  6. js的调试和优化

    一.常见的错误和异常 1.拼写错误 拼写错误,可以有代码的高亮来发现. 2.访问不存在的变量 3.括号不匹配 养成规范的编写习惯,适当应用Tab.空行等. 4.字符串和变量链接错误 采用多加括号来进行 ...

  7. 计算a-b的差[返回BigDecimal 类型]

    /*** * 返回 a-b 的差 [返回 BigDecimal 类型] * @param a 被减数 * @param b 减数 * @return */ public static BigDecim ...

  8. HTTP 文件共享服务器工具 - chfs

    CuteHttpFileServer/chfs是一个免费的.HTTP协议的文件共享服务器,使用浏览器可以快速访问.它具有以下特点: 单个文件,整个软件只有一个可执行程序,无配置文件等其他文件 跨平台运 ...

  9. IDEA设置控制台日志 不换行

    最新版的IDEA设置控制台不自动换行位置如下:Setting->Editor->General->Console,不要勾选下图项即可.

  10. Maven项目的坐标GroupId和ArtifactId

    GroupId和ArtifactId被统称为“坐标”是为了保证项目唯一性而提出的,如果你要把你项目弄到maven本地仓库去,你想要找到你的项目就必须根据这两个id去查找.       GroupId一 ...