RC Immix
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的更多相关文章
- 《垃圾回收的算法与实现》——增量式垃圾回收与RC Immix算法
增量式垃圾回收 为了控制最大暂停时间,通过逐渐推进垃圾回收即垃圾回收与mutator交替执行. 三色标记算法 以标记-清除算法为例使用三色标记算法. 利用降低吞吐量来缩短最大停顿时间. 基础 将GC中 ...
- 深入研究Visual studio 2017 RC新特性
在[Xamarin+Prism开发详解三:Visual studio 2017 RC初体验]中分享了Visual studio 2017RC的大致情况,同时也发现大家对新的Visual Studio很 ...
- Xamarin+Prism开发详解三:Visual studio 2017 RC初体验
Visual studio 2017 RC出来一段时间了,最近有时间就想安装试试,随带分享一下安装使用体验. 1,卸载visual studio 2015 虽然可以同时安装visual studio ...
- Create an offline installation of Visual Studio 2017 RC
Create an offline installation of Visual Studio 2017 RC 2016年12月7日 ...
- Android中的 init.rc文件简介
init.rc脚本是由Android中linux的第一个用户级进程init进行解析的. init.rc 文件并不是普通的配置文件,而是由一种被称为"Android初始化语言"(An ...
- TypeScript 2.0候选版(RC)已出,哪些新特性值得我们关注?
注:本文提及到的代码示例下载地址 - Runnable sample to introduce Typescript 2.0 RC new features 作为一个Javascript的超集, Ty ...
- vs2017 rc 离线安装包制作
vs2017 rc 离线安装包制作 1.下载在线安装包:https://aka.ms/vs/15/release/vs_Enterprise.exe 2.制作离线安装包: vs_Enterprise. ...
- 【Win10 应用开发】解决VS 2015 RC不能调试手机应用的问题
VS2015 RC已发布,当然这个版本还不能用于实际生产中,如果你没有测试环境,就等正式版出来,RC都来了,RTM就不远了. 如果你也像老周一样,已经在耍RC版了,你可能会遇到下面问题: 安装Win ...
- 使用 python 实现 memcached 的启动服务脚本 rc
#!/usr/bin/python #coding:utf-8 import sys import os from subprocess import Popen, PIPE class Memcac ...
随机推荐
- (转载)10个实用的但偏执的Java编程技术
10个实用的但偏执的Java编程技术 在沉浸于编码一段时间以后(比如说我已经投入近20年左右的时间在程序上了),你会渐渐对这些东西习以为常.因为,你知道的…… 作者:小峰来源:码农网|2015-09- ...
- 解决IE7、IE8样式不兼容问题
方法:要在页面中加入如下HTTP meta-tag: <meta http-equiv="X-UA-Compatible" content="IE=EmulateI ...
- 8年js总结
http://www.cnblogs.com/tylerdonet/p/5543813.html
- 3dmax实例教程-使用3ds Max 创建一个完整的场景
本篇教程讲述了利用3ds max创建一个完整的场景. 灵感来源:当我在遇到一些事情睡不着觉的时候我便在努力想象一些别的事情,于是我便想到了这个场景,其实对于我的这个角色我即没有参考图也没有草稿图,有的 ...
- 【BZOJ4940】【YNOI2016】这是我自己的发明
阅读此篇文章前请先跟我大喊三声:dllxl!dllxl!dllxl! 咳咳. 题意: Description 给一个树,n 个点,有点权,初始根是 1. m 个操作,每次操作: 1. 将树根换为 x. ...
- bzoj2333 [SCOI2011]棘手的操作(洛谷3273)
题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作:U x y: 加一条边,连接第x个节点和第y个节点A1 x v: 将第x个节点的权 ...
- 【codeforces 404D】Minesweeper 1D
[题目链接]:http://codeforces.com/problemset/problem/404/D [题意] 让你玩一个1维的扫雷游戏; 游戏的描述由数字0..2以及符号*表示; 分别表示这个 ...
- Python学习简单练习-99乘法表
__author__ = 'ZFH'#-*- coding:utf-8 -*-for i in range(10): #外层循环,range(10),1-9 for j in range(1,i+1) ...
- 【LeetCode OJ 34】Search for a Range
题目链接:https://leetcode.com/problems/search-for-a-range/ 题目:Given a sorted array of integers, find the ...
- 【金阳光測试】基于控件核心技术探讨---Android自己主动化系列(2)---2013年5月
第一讲分享了下安卓自己主动化一些概况和一些自己主动化框架现状和技术可以解决什么样的问题. 这次课就深入到android世界里面.遨游.翱翔.深入了解自己主动化測试核心技术. 搞过编程开发的同学听到in ...