qqwx

导航

垃圾回收算法(4)标记整理

标记-压缩(整理)算法
 
鉴于复制算法中,没有内存碎片的方式能大大提高分配效率,因此,在mark_sweep的基础上,也可以改良出mark_compact算法,使得空闲空间连续。同时又没有复制算法的无帮吃一半堆的问题。
 
首先是最简单的算法,叫Lisp2算法
 
在标记阶段,不变,仍然是对每个活动对象打上标签。
 
随后,开始移动活动对象到堆的开头。因为没有两个堆,又要挪动对象放到一起,那么forwarding指针便需要在对象头中占用空间(复制算法中不用占用是因为它是非活动对象),且在移动前要做一轮forwarding指针的重指。还要做一轮子对象指针的重指。总体流程如下:
compaction_phase() {
  set_forwarding_ptr()
  adjust_ptr()
  move_obj()
}
 
// 设forward指针最简单
set_forwarding_ptr() {
  scan = new_addr = $heap_start
  while (scan < $heap_end)
    if (scan.mark == TRUE)
      scan.forwarding = new_addr
      new_addr += scan.size
    scan += scan.size
}
 
// 调整对象的成员指针,指向成员在堆中的新地址。对于全局变量,因为它们是根的子对象,也需要重指。
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
}
 
// 最后,移动对象
//不列了,比较简单,只需要扫堆,把活动对象向前移,清除forwarding和mark标记即可
 

 

为什么移动对象要放在最后?因为必须先将所有对象的域指针指好才能动对象,否则如果对象先动了,那域指针就乱了
 
优点:
  1. 堆利用效率高
  2. 无碎片
  3. 对象顺序未变
 
缺点:
  1. 搜了3次堆,是标记清除算法的3倍
 
 
针对以上缺点,有人基于partition算法(快速排序的第一步)引入了一个额外的限制,即对象大小整理成一致。提出了two-finger的算法。它只比Lisp2少扫一次堆。
 
分成两步,此处直接描述。
 
第一步,像两根手指分别从左右扫描,找出最右的活动对象和最左的非活动对象,把活动对象拷到左侧指针处。如此往复便规整了整个堆。第一步中有个重要的现象,是最终有效堆指针的右侧,全是“曾经的”活动对象,而移动后这块内存成了空闲堆。因此,可以将forwarding指针放在这些“已移走”的对象中,不需要占用头空间了。
 
第二步,扫描全堆,调整对象的域指针,如果指向右侧一半的空堆,说明要调整,调整也只需要直接指向老地址的forwarding即可(老地址上老对象虽然移走,但forwarding指向了移走对象的目前地址)。并且,这次扫描不需要全堆,只要扫到分界线即可。
 
优点:
  1. forwarding不用了
  2. 少扫堆一次
 
缺点:
  1. 对象的顺序变了,缓存命中效率会小点
  2. 限制:所有对象大小要求一致
 
 
最后,看另一个经典的整理算法:表格法
 
它的思想是,相邻的活动对象一起移动,同时,以表格的形式记录下移动活动对象的起点,及向左移动的偏移量,如[100, 100]指从100开始的活动对象,整理时向左移动了100。问题是这数据记录在哪里?
如果将连续的对象一起移动,那么堆中可以分为两种状态不同的间隔的内存块:活动对象内存块,垃圾内存块。只需要三个指针就可以维护当前的扫描进度关系,垃圾块开头,活动块开头,另一垃圾块开头。只需要将表格信息记录到当前的垃圾块中即可。当后续这块垃圾块被其他活动块移过来覆盖时,再将它移到后面的垃圾块中。
这样,最后,就得到了顺序不变的活动对象仍然在堆的前部。后部是垃圾空间,同时包含着移动信息记录所在的表格。
表格信息是用来更新指针的。
 
因为移动后的对象中的域指针仍是旧值,我们可以遍历表格,从中找到这个旧值当初被移动了多少偏移量,从这个偏移量更新对象中的域指针。
 
优点:
  1. 同样不需要多余空间存forwarding指针
  2. 同时又保持顺序
缺点:
  1. 移动对象时,很可能也要移动表格
  2. 最终计算指针偏移时,要大量扫描表格

posted on 2017-04-01 00:41  qqwx  阅读(1462)  评论(0编辑  收藏  举报