读书人

关于垃圾回收的一个有关问题

发布时间: 2012-03-04 11:13:34 作者: rapoo

关于垃圾回收的一个问题
比较典型的垃圾回首策略有两种:
1、引用记数(Reference Counting)
2、追踪回收(Mark-Sweep)
众所周知,引用记数存在交叉引用的问题;

很多大师写的书中讲到追踪回收可以避免这个问题,
但反复思考,感觉追踪回收同样也存在交叉引用的问题。

假设用链表来组织管理内存,链表中每个结点除了包含实际
使用的内存空间外,还包含其它的一些信息,比如使用状态,
“占用”或者“空闲”。
追踪回收的思想在于:
1、每次进行垃圾回收之前,先将所有状态为“占有”
的结点的状态修改为“测试”;
2、遍历进程空间(为简化起见,不考虑多线程),包括数据段、
堆、栈地址空间,逐一检查地址,如果地址指向状态为“测试”
的结点,则将结点的状态修改回“占用”;
3、遍历完之后,重新遍历链表,清楚所有状态为“测试”的结点,
因为这些结点都是垃圾,需要回收。

以上就是对追踪回收的简单描述,很显然,同样存在交叉引用的问题。
如果某个结点内部存在一个指针指向另一个结点,反之另一个结点内部
也存在一个指针指向该结点,那在上述第2步中,这两个结点的状态都会
修改回“占用”,从而永远不会被回收。

不知道各位大虾如何理解这个问题?




[解决办法]
2、遍历进程空间(为简化起见,不考虑多线程),包括数据段、
堆、栈地址空间,逐一检查地址,如果地址指向状态为“测试”
的结点,则将结点的状态修改回“占用”;


从其它指针开始检查而不是从节点发起。即如果有 A-> B B-> C C-> A,那么除非有非动态的指针指向三个节点之一,否则这三个节点都会被回收。

举例来说,定义两个函数 fa和fb 如下:
fa()
{
A * pa;
pa = new A(new B(1)); //A-> B ,B-> A
fb();
//1 执行到这里开始垃圾回收
....
}

fb()
{
A * pb;
pb = new A(new B(2));//A-> B, B-> A
}

那么,当执行到//1 处时,

pb已经无效,因此不会处理它所指向的节点;于是pb指向的A的对象仍为测试态,最终被销毁;同时A对象内部指向的B的对象也不会被找到,于是也被销毁;

pa仍然有效,于是标记它指向的A的对象为占用;顺藤摸瓜,这个A对象内部指向的B的对象也被标记为占用,于是不会被销毁。

读书人网 >C++

热点推荐