GC(垃圾回收)保守式GC

垃圾回收算法可以分为两类,保守式GC和准确式GC。保守式GC指的是:不能识别指针和非指针的GC,而保守就保守在:将非指针指向的对象视为活动对象并且不废弃处理。准确式是指:能够识别保守式GC和准确式GC。

我们先来了解一个概念:不明确的根。实际上寄存器,调用栈,全局空间变量。这些都是不明确的根。因为寄存器,调用栈,全局空间变量这三者都能包含指针和非指针,所以GC扫描到他们的时候就不知道里面存的到底是指针还是非指针。比如说调用栈,调用栈里面就有调用帧,调用帧包含局部变量和参数,局部变量就有例如int,double的非指针也有void*这样的指针。

在不明确根这种条件下,保守式GC就不能准确识别指针和非指针。不能准确是说明还是有一套识别的方式的。保守式GC识别不明确根时执行3个项目,当3个项目同时成立时则说明是指针。这3个项目是:

1,是不是被正确对齐?

2,是不是指向堆?

3,是不是指着对象的开头?

下面我们展开谈谈这三个项目:

1,是不是被正确对齐?这是利用CPU的对齐来检查的。若32b的CPU,指针的值的位数==地址的位数==4的倍数,若64b的CPU,指针的值的位数==地址的位数==8的倍数.这个项目,我们必须在语言处理程序中使用指针符合对齐的规则(对齐这个概念和计算机组成原理内存对齐存放是一样的)

2,是不是指向堆?一般来说,对象分配的地址空间一定是在堆空间,而若指针指向的不是堆就可以判断一定不是指针。

3,调查不明确的根是不是指着对象的开头,具体我们可以使用BIBOP法,把对象按照固定大小对齐,核对检查对象的值是不是对象固定大小的倍数。

这三个项目可以判断出大多数非指针,但总是有例外的误判。例如:非指针和堆里的对象的地址是一样的情况,这时保守式GC就无法识别这个是非指针,而把这个非指针视为指针并且指向的对象一律判为活动对象。再例如:存在不明确的数据结构时

struct {
    long c;
    void* ptr

}ambiguous_data

这里面既存在指针ptr也存在非指针c。若c是非指针和堆里的对象的地址是一样的情况,那么GC也无法识别C是个非指针。当对象是这样的数据结构时,GC不仅会错误识别不明确的根,也会错误识别域里的值。

总结,保守式GC的优点:只有简单,简单就意味着不容易出BUG。

缺点:识别指针和非指针需要以上3步,成本有点高。错误识别指针会压迫堆导致没有清除垃圾的效果。不能使用移动式的垃圾回收算法,例如复制算法,因为复制对象的时候会重写指针来指向新的空间,而当错误将非指针识别成指针时重写的就是非指针。