您的位置:首页 > 编程语言 > Java开发

Java JVM 2:垃圾收集算法 - 标记整理算法(伪代码实现与深入分析)

2017-04-09 19:52 856 查看
算法原理

标记整理法首先需要标记出存活的对象,然后所谓的整理就是把这些存活的对象往一端推。然后就清除边界以外的区域即可。

老年代的垃圾回收器(例如 Serial Old,Parallel Old,到那时不包括CMS,CMS使用的是标记清除法)都是采用这个算法,主要由于老年代的对象都比较持久,不是短暂的。

这样一看,每次整理,将不会产生内存碎片问题,因为也没有分配对象需要查空闲链表了。

伪代码实现

只要涉及到对象的移动,都会存在一个问题,就是假如 A -> B(A 引用 B,A 移动后的对象为 A’,B 移动后的对象为 B’,),那么怎么保证 A’-> B’。 其实这里还是利用了一个对象的属性 forwarding(参考:Java JVM 3:垃圾收集算法 - 复制算法(伪代码实现与深入分析))。

forwarding 的意思就是 它是 A对象的一个属性,然后记录了 A 对象移动到 A’。forwarding 会记录这个 A’,但是实际上还没移动,就是先记录好。

整个过程分 3 步:

首先,需要设置这个 forwarding,用于知道 A 后面移动到 A’,B 后面移动到 B’,这样才有可能把 A’ -> B’ 关联上。

set_forwarding_ptr(){
scan = new_address = $heap_start
while(scan < $heap_end){
if(scan.mark == TRUE){
scan.forwarding = new_address
new_address += scan.size
}
scan += scan.size
}
}


这段代码感觉还算比较好理解的,也就是记录自己移动后是到哪个位置:scan.forwarding = new_address。(注意,这里只是预先记录一下,还没有真正开始移动)

步骤二,更新指针:

adjust_ptr(){
for(r : $roots){
*r = (*r).forwarding
}

scan = $heap_start
while(scan < $heap_end){
if(scan.mark == TRUE){
for(child : children(scan)){
*child = (*child).forwarding
}
}
scan += scan.size
}
}


这段代码主要说的就是重写 A’ 对象移动后指向哪个对象。例如 *child = (*child).forwarding ,这里可以看到 A原来是引用B,(*child).forwarding 表示 B 到时候会变成 B’,此时需要把 child 改成以后的 B’ ,那么,此时在 A对象中,已经记录了之后A会变到 A’(forwarding指针记录了),然后上面那个代码又知道了引用的 B 对象以后要变到哪里去,所以说,最后移动的时候,A’ -> B’就很容易关联上。

步骤三:真正的移动

move_obj(){
sacn = $free = $heap_start
while(scan < $heap_end){
if(scan.mark == TRUE){
new_address = scan.forwarding
copy_data(new_address,scan,scan,size)
new_address.forwarding = NULL
new_address.mark = FALSE
$free += new_address.size
scan += scan.size
}
}
}


这段主要就是先 copy:copy_data(new_address,scan,scan,size) ,然后位置一直往前挪动:$free += new_address.size

优缺点

耗费的时间在哪里

1、系统怎么知道A对象后来去到 A’ ,那么,这个是需要遍历整个堆的。比如说,发现第一个位置需要GC了,然后 A 不需要,那么就把 A.forwarding 设置到第一个位置这里。所以这里要遍历一遍堆。

2、设置 A’ -> B’ 的关联。这个从上面代码实现来看也要遍历一遍堆:while(scan < $heap_end)。但是个人表示怀疑,因为根据 forwarding 知道了 A 变到 A’ ,也能由A得到 B,再由 B 的 forwarding 可以得到 B’,所以感觉这里应该可以设置 A’ -> B’。

3、移动对象,这个是肯定要遍历一遍堆的。因为涉及到移动,清除。

所以总的来说,这个算法的效率无论比起复制算法还是标记清除法,遍历的次数都是要多一点。

没有内存碎片问题

所以说分配内存的时候也不需要到空闲链表这个东西
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  虚拟机 jvm