Two-Finger算法

Robert A.Saunders 对堆执行两次搜索

前提

Two-Finger 算法,必须将所有对象整理成大小一致。它没有在对象的头中设立forwarding指针,而是在对象的域中设立forwarding指针即可。

概要

Two-Finger算法由一下两个步骤构造。

  • 移动对象
  • 更新指针

在Lisp2算法中,是将对象移动到堆的一端。在Two-Finger中,操作对象向左滑动,通过执行压缩算法来填补空闲空间。此时为了让更好的填补空间,所以对象大小必须一样。

移动前的对象都会被保留(图的白色对象)。因为在Two-Finger算法中,我们要利用放置非活动对象的空间来作为活动对象的目标空间,这是为了让移动前的对象不会在GC过程中被覆盖掉。这样一来,我们就能把forwarding指针设定在这个移动前 的对象的域中,没有必要多准备出 1 个字了。

步骤一:移动对象

  • $free和live两个指针,从两端向正中间搜索堆。
  • $free是用于寻找非活动的指针,live是寻找活动对象(原空间)

  • 两个指针发现空间和原空间的对象时会移动对象。

  • 途中虚线其实表示forwarding。
  • 之后使用move_obj函数对对象进行移动其伪代码如下:
move_obj(){
$free = $heap_start
live = $heap_end - OBJ_SIZE
while(TRUE)
while($free.mark == TRUE) //从前往后寻找非活动对象
$free += OBJ_SIZE
while(live.mark == FLASE) // 重后往前 寻找活动对象
live -= OBJ_SIZE
if($free < live) // 判断交换条件
copy_data($free, live, OBJ_SIZE)
live.forwarding = $free
live.mark = FALSE
else
break
}
  • 先从前往后,使用$free寻找非活动对象。
  • 在从后往前,使用live寻找活动对象。
  • 找到之后,判断两者位置。如果非活动对象在活动对象之前,就执行复制操作。否则就退出循环。

步骤二:更新指针

接下来寻找指向移动前的对象的指针,把它更新,使其指向移动后的对象。更新指针操作的是adjust_ptr()函数。

  • 当对象移动结束时,$free 指针指向分块的开头,这时位于 $free 指针右边的不是非活动对象就是活动对象。
  • $free右边地址的指针引用的是移动前的对象。
adjust_ptr(){
for(r :$roots)
if(*r >= $free)
*r = (*r).forwarding scan = $heap_start
while(scan < $free)
scan.mark = FALSE
for(child :children(scan))
if(*child >= $free)
*child = (*child).forwarding
scab += OBJ_SIZE }
  • 先查询根直接引用的对象。当这些指针的对象在$free右边的时候,就意味这个对象已经被移动到了某处。在这种情况下必须将指针的引用目标更新到移动后的对象。
  • 所有活动对象都在$heap_start 和 $free之间,我们需要取遍历这一部分堆。

优缺点

优点:Two-Finger 算法能把 forwarding 指针设置在移动前的对象的域里,所以不需要额外的内存 空间以用于 forwarding 指针。只需要2次搜索堆

缺点: Two-Finger 算法则不考虑对象间的引用关系,一律对其进行压缩,结果就导致对象的顺序在压缩前后产生了巨大的变化。因此,我们无法更好的使缓存。 对象大小必须一样

表格算法

B.K.Haddon 和 W.M.Waite, 1967

这个算法使用表格来进行压缩,和Two-Finger一样都是执行两次压缩。

概要

表格算法通过以下2个步骤来执行压缩。

  1. 移动对象以及构筑间隙表格(break table)
  2. 更新指针

步骤1是让连续的活动对象群一并移动。(和前面所接触到的压缩算法都不同)。除此之外还要预留更新指针所用到的信息,这里我们使用间隙表格。

间隙表格,大概意思是“按照一个个活动对象群记录下压缩所需要的信息的表格”。这个表格事先放入移动前的对象群信息(位于对象群的首地址和较低地址的分块的总大小)。为了方便地址计算,我们将1个字的大小定为50.如下图示:

  • 各个入口左边的值是活动对象群的首地址,右边的值是分块的总大小。随着对象的移动,它会被放置在空闲空间里。不过,间隙表格的各个入口需要2个字节。也就是说,这算法的其中一个限制条件就是,每个对象都必须在2个字节以上。
  • 步骤2,更新每个指针。

步骤一:移动对象群 和 构筑间隙表格

移动对象群

活动对象群移动前和移动后(move_obj())的状态如图示:

move_obj(){
scan = $free = $heap_start
size = 0
while(scan < $heap_end) // while 循环 1
while(scan.mark == FALSE)
size += scan.size
scan += scan.size
live = scan
while(scan.mark == TRUE) // while 循环 2
scan += scan.size
slide_objs_and_mark_bt(scan ,$free, live, size)
$free += (scan-live)
}
  • scan用于寻找活动群对象,从堆头开始搜索。
  • $free是指向对象群目标空间的指针,size是保持分块大小的变量。这里的分块是指用来记录到间隙表格里的分块。
  • 第一个while中,scan指针负责寻找活动对象的群的开头。也就是说,直到它寻找到活动对象为止,都会跳过非活动对象。于此同时使用size计算scan指针跳过的空间大小。
  • scan是指针,size是整数。
  • 搜索结束时候,scan指针指向活动对象群开头。这个为止记录在live指针里。
  • 之后继续使用scan,搜索连续的多动对象群。(一次找对活动对象循环完毕)

这时,堆的状态如下图示

在第二个while循环中

  • 其中slide_objs_and_make_bt()函数中执行活动对象群和构筑间隙表格的操作。对象群的原空间是live,目标空间是$free,要移动的对象群的总大小是scan-live。
  • 最后一行,准备下一次移动,将$free移动scan-live个大小,即$free向后移动大小等于对象群的大小。如下图示:

  • 这里和Lisp2算法一样,都是通过把活动对象左滑压缩。不过这里是移动连续的对象群。

构筑间隙表格

在上一个图中,每次移动对象群的时候都需要吧信息注册到间隙表格中。注册入口是对象群的首地址live对象群滑动大小size的组合。如下图示:

构筑间隙表格是在slide_objs_and_mark_bt()方法中指向的,下面使用图例来说明过程:

  • 间隙表格构筑有以下两项操作构成。

    • 移动对象群
    • 移动间隙表格
  • 下图示()内数字表格各个对象的首地址,设1个字的大小为50

  • 如中a部分,在移动对象群BC的同时构筑间隙表格。将BC的首地址100以及BC最左边的分块大小100组合成一对,通过scan指针写入已知分块的350号地址。
  • b部分,在这里进行的是移动对象群FG的操作。这时候要注册到间隙表的的信息是(550, 300)不过不能直接将该信息写入(100, 100)之后(450块)因为对象群要移进去。
  • 这时候,我们有的间隙表格移动到FG后面也就是700号地址里。
  • 完成后状态如e所示。注意间隙表格的入口顺序,各个入口不是按入口里的第一元素排列的,也就是说,不是按活动对象群的首地址live进行排列的。
    • 在b中,因为间隙表格妨碍到对象FG的移动,所以先让它回避到800,之后在移动FC将新的表格注册到了700上。
    • 像这样往已有的间隙表格中新追加入口时,会有表格左侧空闲的情况,在这种情况下,入口顺序只能乱了。
    • 当然也可以按照顺序排列,如论是在添加的时候按顺序,或者添加完之后排序,它都要花费一定的空间和时间。
  • 因为没有按照第一元素live的顺序排列,所以增大了更新指针的计算量。

步骤二:更新指针

在dajust_ptr()函数中,将引用移动前的对象的指针全部换成引用移动后的对象的指针。这项操作本身和前面的两个算法中的操作是相同的。

adjust_ptr(){
for(r :$roots)
*r = new_address(*r) scan = $heap_start
while(scan < $free)
scan.mark = FALSE
for(child : children(scan))
*child = new_address(*child)
scan += scan.size }

下面是new_address(obj)函数

new_address(obj){
best_entry = new_bt_entry(0, 0)
for(entry :break_table)
if(entry.address <= obj && $best_entry.address < entry.address)
best_entry = entry
return obj - best_entry.size }
  • 这个函数返回参数obj移动后的地址,在其中new_bt_entry(0, 0)函数中生成虚拟间隙表格的入口。
  • for循环负责调查间隙表格,在持有obj及其一下地址的入口中寻找最大的入口。这样一来就得到了持有obj所属对象群信息的入口。这个入口就是best_entry
  • 如果间隙表格里的入口是按照地址顺序整齐排列的,我们就有可能用二分查找有效地址查询到best_entry。但是间隙表格的入口并不是整齐排列的,因此就需要通过上面这种方式来查找。
  • best_entry是一个入口,这个入口持有obj所属对象群移动前的信息。属于这个对象群的对象都会被向左移动best_entry.size个大小。因此obj移动后的地址变成了obj-best_entry.size。

上图中如果我们想知道B移动到了BꞋ,首先就要以B的地址100为线索调查间隙表格,然后就会发现入口(100,100)是best_entry,接下来可由B的地址 100 求得 best_entry.size,即将 B 的地址减去 100 得到 BꞋ 的地址 0。 同理,我们可以从 F 的地址 550 减去入口(550,300)中的 300,得到 FꞋ 的地址 250。

优缺点

优点:算法很好地利用了分块,保留了更换指针所必要的信息。(没有为压缩备出多余空间,)并且它没有改变对象的顺序,所以可以通过缓存来提高对象的访问速度。

缺点:维持间隙表格需要付出很高的代价,每次移动对象群都要对表格进行操作。

Mark Compact GC (Part two :Two-Finger)的更多相关文章

  1. Mark Compact GC (Part one: Lisp2)

    目录 什么是GC 标记-压缩算法 Lisp2 算法的对象 概要 步骤 步骤一:设定forwarding指针 步骤二:更新指针 步骤三:移动对象 优缺点 什么是GC 标记-压缩算法 需要对标记清除和GC ...

  2. Mark Sweep GC

    目录 标记清除算法 标记阶段 深度优先于广度优先 清除阶段 分配 First-fit.Best-fit.Worst-fit三种分配策略 合并 优点 实现简单 与保守式GC算法兼容 缺点 碎片化 分配速 ...

  3. 1. GC标记-清除算法(Mark Sweep GC)

    世界上第一个GC算法,由 JohnMcCarthy 在1960年发布. 标记-清除算法由标记阶段和清除阶段构成. 标记阶段就是把所有的活动对象都做上标记的阶段. 标记阶段就是"遍历对象并标记 ...

  4. Gson序列化问题导致的内存溢出,tip:Background sticky concurrent mark sweep GC freed

    问题原因,如果在json model里面放了非可序列化的对象就会导致这中问题,可序列化的就是那些基础数据类型和集合类型,如果在里面放个Android的Activity或者adapter这类类型字段,变 ...

  5. copy GC 和 mark & compaction GC的算法异同

    先标记 然后 copy GC是,对所有child,判断, 如果child没有被访问过,那么拷贝到新地址,child的forwording指向新地址,child标记为已访问,把自己对child的引用改为 ...

  6. GC学习笔记

    GC学习笔记 这是我公司同事的GC学习笔记,写得蛮详细的,由浅入深,循序渐进,让人一看就懂,特转到这里. 一.GC特性以及各种GC的选择 1.垃圾回收器的特性 2.对垃圾回收器的选择 2.1 连续 V ...

  7. 非常详细GC学习笔记

    转载:http://blog.csdn.net/fenglibing/article/details/6321453 这是我公司同事的GC学习笔记,写得蛮详细的,由浅入深,循序渐进,让人一看就懂,特转 ...

  8. 很具体GC学习笔记

    GC学习笔记 这是我公司同事的GC学习笔记,写得蛮具体的,由浅入深,循序渐进,让人一看就懂,特转到这里. 一.GC特性以及各种GC的选择 1.垃圾回收器的特性 2.对垃圾回收器的选择 2.1 连续 V ...

  9. gc学习(转)

    一.GC特性以及各种GC的选择 1.垃圾回收器的特性 2.对垃圾回收器的选择 2.1 连续 VS. 并行 2.2 并发 VS. stop-the-world 2.3 压缩 VS. 不压缩 VS. 复制 ...

随机推荐

  1. Android中处理Touch Icon的方案

    苹果的Touch Icon相对我们都比较熟悉,是苹果为了支持网络应用(或者说网页)添加到桌面需要的图标,有了这些Touch Icon的网页链接更加和Native应用更相像了.由于苹果设备IPod,IP ...

  2. RIP协议的配置

    拓扑图: 要求: 理解RIP协议的工作原理,并通过 RIP 路由协议实现不同网络互通. 配置步骤: 1.配置好 PC 机的 IP 地址等參数. PC 0>ipconfig IP Address. ...

  3. leetCode(38):Lowest Common Ancestor of a Binary Search Tree

    Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BS ...

  4. 阿里云X-Forwarded-For 发现tomcat记录的日志所有来自于SLB转发的IP地址,不能获取到请求的真实IP。

    1.背景:阿里云的SLB的负载均衡,在tomcat中获取不到真实IP,而是阿里的内网IP,SLB中俩台或者3台本身是局域网,这里是SLB原理,能够看看.没怎么看懂.呵呵,要细细读下. 2.须要开启to ...

  5. angularjs 标签指令

    <!DOCTYPE HTML> <html ng-app="myApp"> <head> <meta http-equiv="C ...

  6. zzulioj--1775-- 和尚特烦恼1——是不是素数(素数水题)

    1775: 和尚特烦恼1--是不是素数 Time Limit: 2 Sec  Memory Limit: 128 MB Submit: 563  Solved: 193 SubmitStatusWeb ...

  7. sicily 1031 Campus(图算法)

    Description At present, Zhongshan University has 4 campuses with a total area of 6.17 square kilomet ...

  8. Python开发注意事项

    仅为记录自己在使用python过程的的一些心得!   1.服务器上运行脚本: windows服务器: 显式运行:在cmd中直接用python xxxx.py  运行一个py脚本文件. 后台运行:在cm ...

  9. [国家集训队]拉拉队排练 Manancher_前缀和_快速幂

    Code: #include <cstdio> #include <algorithm> #include <cstring> using namespace st ...

  10. iOS-入门HelloWorld

    刚刚搞了几个图形界面的iOS应用程序,难的没搞定一个,HelloWorld程序倒是很简单. 新建Project,iOS->Application->Single View Applicat ...