关于保守式GC

Catalogue
  1. 1. 保守式gc的示例
  2. 2. 保守式gc的缺陷

github: https://github.com/brewlin/garbage-collect

当前所有的gc实现都是基于保守式gc实现的,特点就是实现简单、容易理解,对应用层友好

保守式gc的示例

比如一个内存分配如下:

1
2
3
4
5
6
7
8
9
#include "gc.h"
void test(){
typedef struct obj{
.....
}Obj;

Obj* p = gc_malloc(sizeof(Obj);
// do something else
}

没有任何依赖,只需要调用gc_malloc()分配一块空间即可,不需要应用层去操心如(标记,内嵌指针啥的),实现简单

保守式gc的缺陷

在这简单的背后自然也有保守式gc的一个缺陷,那就是不能识别指针和非指针。接着上上面的函数来说,举个例子

1
2
3
4
5
6
7
void main(){
test();
double p = 0x555555;

//内存不够了 自动执行gc
void* pp = gc_malloc(size);
}

  1. 调用test函数,加入该函数内p的指针刚好也是 0x555555
  2. test函数返回后继续执行,此时栈上有一个double变量,且值刚好也是0x555555 和上面test函数的指针值相同
  3. 假如再次申请空间时,内存不够了,默认启动gc垃圾回收
  4. 首先: test作用域已经退出了,在进行root扫描时(可以先去看什么是root?) Obj *p已经是不可达对象,称为垃圾对象
  5. 但是: 在main中root扫描,扫到了 double p 且刚好该值是我们实现的堆里的一个合法地址
  6. 最终: 导致test的p本来该回收,但是因为 double p导致了回收失败

这就是保守式gc,某些情况下会无法准确识别指针 or 非指针,导致内存得不到释放

当然现代很多语言都是基于保守式gc,也有很多对策来降低这种误差