程序变量中任何指针链无法到达的堆分配记录都称为垃圾信息。而被垃圾信息填满的内存空间应该收集,以便重新分配新的记录,而这个过程就叫做垃圾信息回收,它是编译器的后端来执行的。对于以下一个Java模型可以转换为c代码:
//java code
class A{
B x;
int y ;
C z;
int fuc(int a,B b,D d){
E e;
int m;
F f;
}
} <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
// c code
Struct A{
Struct A_vatable; //class A的虚函数表
Struct B *x;
int y;
Struct C *z;
};
Struct A_vatable{
int (*fuc)(); //通过它来调用A_fuc()函数
};
int A_fuc(A this,int a ,B b,D d){
//基本和fuc是一样的,A this主要是要用A的某些属性或者方法
}
}
在做复制收集的时候,以记录作为节点,指针作为边,程序变量作为根,堆中的可到达部分可以用一张直观的图来表示。垃圾回收时遍历这张图(在堆中称为from-space),然后在堆得一个新区域中(to-space)建立一个同构复制。to-space的复制时压缩的,它连续而没有分段的。
现在我们假设在Java code的int fuc()中E e时发现from-space不够,需要进行垃圾回收。那么我们如何进行上述所说的遍历呢,这里我们要引入一个函数gc栈的数据结构。对于函数f()来说
它的gc栈应该是T f (D this, int arg1, A arg2, B arg3){ A local1; int local2; C local3; ... // statements }
这样相应的f()函数中加入其gc栈要变化为struct f_gc_frame{ void *prev; // 指向调用f()的函数的gc栈 char *arguments_gc_map; // 表示f()形参的引用变量个数,这里应该是"1011",D this是引用变量,所以是'1',int arg1是整形所以是'0' int *arguments_base_address; // f()函数的第一个形参的地址(即D this),这样就可以通过它和char *arguments_gc_map取到所有的形参 char *locals_gc_map; // f()函数局部变量的引用表示,这里应该是"101" struct A *local1; // 把f()所有的局部变量放进来 int local2; struct C *local3; };
现在先通过遍历当前prev的所有引用型变量,然后在遍历prev.prev的所有引用型变量就把所有的根遍历了。这样就可以把他们从from-space全部都copy到to-space中。然后在在to-space中遍历所有对象中的引用,看是否拷贝到to-space即可。T f (D this, int arg1, A arg2, B arg3){ struct f_gc_frame frame; // push this frame onto the GC stack by setting up "prev" frame.prev = prev; prev = &frame; frame.arguments_gc_map = "1011"; frame.arguments_base_address = &this; frame.locals_gc_map = "101"; // initialize locals of this method ... // statements should be rewritten apporpriately
}