GC复制算法

###GC复制算法

它是把某一空间的活动对象全部复制到另一个空间,复制完成后GC也就结束了

一起看下GC复制算法的copying函数:


  1. copying(){
  2. $free = $to_start //将free设置在To空间的开头
  3. for(r : $roots) //可以复制从根引用的对象
  4. *r = copy(*r) //将函数作为参数传递的对象*r复制的同时,将子对象进行递归调用
  5. swap($from_start,$to_start) //复制结束返回指针后交换指向空间
  6. }

copy函数在复制时会先检查是否已被复制,若已被复制,不再操作,否则进行复制,贴上copied标签复制完成返回新空间的地址,这样即使有多个对象引用obj,也不会被被多次复制

还有一点就是把指向新空间对象的指针放到obj.forwarding里,这样之后找到指向原空间对象的指针时,需要把找到的指针指向新的空间,forwarding指针就是为它准备的

要实现这个方法,必须满足2个条件

1)每个对象至少有2个域,分别存贮copied标签和forwarding指针

2)copied标签为了挪用obj中的域,必须选一个mutator绝不会用到的值


GC复制算法的分配过程很简单


  1. new_obj(size){
  2. if($free + size > $from_start + HEAD_SIZE/2) //HEAD_SIZE是from和to的空间大小总和,from和to大小一样,所以都是HEAD_SIZE/2
  3. copying() //这个函数会把从根引用的对象及其子对象复制到to空间
  4. if($free + size > $from_start + HEAD_SIZE/2)
  5. allocation_fail()
  6. obj = $free
  7. obj.size = size
  8. $free += size
  9. return obj //GC完成后只有1个分块的内存空间,每次分配只需要把申请的内存空间从这个块分割出来分给mutator就好了,与标记-清除算法不同的是,不需要遍历空闲链表
  10. }

复制的过程用的是深度优先搜索


优点:

1)优秀的吞吐量

GC标记-清除算法消耗的吞吐量是搜索活动对象(标记阶段)和搜索整体堆(清除阶段)所花费的时间之和,GC复制只搜索并复制活动对象,可在较短时间内完成GC,也就是吞吐量优秀

2)可实现高速分配

GC复制算法不使用空闲链表,因为分块是一个连续的空间,只要申请的空间小于分块,就可以移到free指针进行分配了,使用空闲链表时,不得不变量空闲链表,找到符合要求的分块,无疑浪费了大把的时间

3)不会产生碎片

把所有对象集中放在堆的一端的行为叫压缩,GC复制时,每次都会进行压缩,所以,不会产生碎片化

4)与缓存兼容

由于是深度优先搜索,所以,有引用关系的对象会被防止距离较近的位置,这种情况下mutator就会执行比较快,这也是借助压缩实现的优点


缺点:

1)堆使用效率低,它把堆二等分,只能使用其中的一般来存放对象了

2)不兼容保守式GC算法

GC标记-清除法不用移动对象,所以可以和保守式GC相兼容,GC复制算法,必须移动对象,重写指针,所以没办法兼容,不过,可以组合使用

3)递归调用函数

复制某个对象时,要递归复制其子对象,所以每次进行复制的时候都要调用函数,由此带来的负担不容忽视,此外,每次递归调用都会消耗栈,所以,还有栈溢出的可能


#### cheney的GC算法

和上边的大致相同,但是省略了copied标签,利用forwarding指针来标记是否复制完成,如果有指向to空间的指针,则复制完成,另外使用的是广度优先搜索(先复制所以从根引用的对象,再查找对to空间的对象有引用的对象)


优点:cheney的GC是迭代,可以抑制调用函数的额外负担和栈的消耗,特别是拿堆用作队列,省去了搜索的内存空间,这点是点睛之处


缺点:失去了深度优先搜索时的缓存利用的遍历,有引用关系的对象并不相邻


### 近似深度优先搜索

先广度搜索,使有引用关系的对象在相同的页出现,然后,进行深度优先搜索,使得不管在哪个页面,对象间都有引用关系


#### 多空间复制算法

其实就是把堆N等分,对其中2块进行GC复制,另外的空间进行GC标记-清除算法

优点:将堆分割成更多的空间,有效的利用了堆,多空间的复制算法仅需要空出一个分块,不能使用的只有1/N个堆


缺点:由于是GC复制算法和标记-清除算法的组合,标记清除算法固有的缺点没办法避免,比如分配耗时,碎片化的产生




你可能感兴趣的:(GC垃圾回收的算法和实现)