随笔 - 119  文章 - 290  trackbacks - 0

博客搬家了哦,请移步
叫我abc

常用链接

留言簿(12)

随笔分类

我的博客

搜索

  •  

积分与排名

  • 积分 - 302254
  • 排名 - 84

最新评论

阅读排行榜

到昨天为止,yfgc中的核心代码,我觉得是看完了。
gc_malloc中,了解到分配出来的内存如何进行管理;
gc_link中,了解到如何在两块内存之间建立引用依赖关系;
gc_entergc_leave中,了解到通过gc_malloc分配出来的自由内存是如何在调用堆栈上记录的;
gc_collect中,整合了以上所有的内容,了解到整个依赖树是如何建立固定部分和自动部分,以及如何标记出垃圾和非垃圾内存的区别并进行回收的。
不过,在gc_collect的实现中,还是有些WEAK_CONTAINER的内容无法理解,因此今晚就来看看weak table吧。

weak table我译为弱表,什么是弱表呢?弱表可以看作一个容器,容器内的元素为其他内存的指针,也就是容器和其他内存之间建立了引用依赖关系。只是这种引用是弱引用,其依赖关系通常会在gc_collect的时候被系统自动解除,以便于将容器中所指向的内存进行回收(如果这些内存没有在其他地方被强引用的话)。因此弱表引用的内存通常不能直接持有,因为其生命期是没有保证的,只能通过弱表进行查找访问。我的解释可能不太准确,有兴趣的同学自己google一下吧。
解释了概念,接下来看看代码

 1struct gc_weak_table {
 2    int node_id;
 3}
;
 4
 5struct gc_weak_table*
 6gc_weak_table(void *parent)
 7{
 8    struct gc_weak_table *ret=my_malloc(sizeof(*ret));
 9    ret->node_id=map_id(ret);
10    E.pool[ret->node_id].u.c.weak=WEAK_CONTAINER;
11    if (parent) {
12        gc_link(parent,0,ret);
13    }

14    else {
15        stack_push(ret->node_id);
16    }

17    return ret;
18}
首先,gc_weak_table的成员变量node_id,用来保存他在 E.pool中的索引。保存这个的作用是,用来定位弱表中所引用的内存的存放位置,看gc_weak_next的时候就知道了。
至于gc_weak_table函数的实现非常熟悉,其实就是gc_malloc,再加上一些特殊代码罢了。第9行,对自身内存的id赋值。第10行,用WEAK_CONTAINER将自己的节点标记为弱表节点,这样在gc_collect的时候就会得到特殊照顾了,这点后面再说。

 1void* 
 2gc_weak_next(struct gc_weak_table *cont,int *iter)
 3{
 4    int i,j;
 5    struct link *children = E.pool[cont->node_id].u.n.children;
 6    if (children==0{
 7        return 0;
 8    }

 9
10    for (i = (iter==0 ? 0 : *iter) ;i < children->number; i++{
11        int id=children->children[i];
12        if (id) {
13            if (E.pool[id].u.c.mem == FREED_POINTER) {
14                children->children[i] = 0;
15            }

16            else {
17                if (iter) {
18                    *iter=i+1;
19                }

20                stack_push(id);
21                return E.pool[id].u.n.mem;
22            }

23        }

24    }

25
26    for (i=0;i<children->number;i++{
27        if (children->children[i]==0{
28            break;
29        }

30    }

31
32    for (j=i,++i;i<children->number;i++{
33        if (children->children[i]!=0{
34            children->children[j++]=children->children[i];
35        }

36    }

37
38    children->number=j;
39
40    return 0;
41}
gc_weak_next用来遍历弱表中所有引用的内存,参数cont即弱表本身,参数iter为弱表所引用的内存的位置索引,通常初始值为1,被gc_weak_next递增改变。
从第5行可知,其实弱表所引用的内存就在children中。第10行的for循环体,遍历children查找一块还有效的引用内存。第20行将他stack_push,因为这是要return的,最少会被外部所持有,因此压入自由内存堆栈,稍微延长生命期。
剩下的代码,是用来删除已经被回收的child内存(对应的chidren[i]=0),稍微看看就能理解了。

弱表的代码就这么多,通过gc_weak_next的实现,可以得知,仍旧是通过gc_link来将内存添加到弱表中的,只要弱表是parent即可。
好了,现在可以回头看看在gc_collect中和弱表有关的代码了,如果没有弱表,整个gc库的if、else什么的会少得多。
 1static void
 2gc_mark(int root)
 3{
 4            if (E.pool[root].u.c.weak==WEAK_CONTAINER) {
 5                for (i=children->number-1;i>=0;i--{
 6                    gc_mark_weak(children->children[i]);
 7                }

 8            }

 9}

10
11static __inline void
12gc_mark_weak(int weak)
13{
14    if (E.pool[weak].mark <  E.mark) {
15        E.pool[weak].mark=E.mark;
16    }

17}
在gc_mark对依赖关系做标记的时候,有一个关于WEAK_CONTAINER的分支判断,对弱表所引用的内存节点执行的是gc_mark_weak而gc_mark_weak的实现则是将节点的mark赋值为E.mark。这也就解释了为什么在gc_collect的时候,会存在E.pool[i].mark == E.mark的情形,这是弱表所引用的内存被标记的结果,因此弱表所引用的内存,在gc_collect的时候就被回收,不过并没有用node_free释放对应的内存节点,而是做了一个标记
E.pool[i].u.c.mem=FREED_POINTER;
这样这些节点不会在下一次gc_collect的时候被回收。因为弱表的children还引用着他们,维护着依赖关系,即使分配的内存已经回收了。只有用gc_weak_next遍历了弱表之后,这些废弃的管理节点才会从children中被删除,最终在gc_collect的时候被回收。
为啥非要主动遍历弱表才能这样呢。。。。
posted on 2008-09-23 22:33 LOGOS 阅读(3786) 评论(2)  编辑 收藏 引用 所属分类: 垃圾收集

FeedBack:
# re: 垃圾收集的那点事(K) 2009-03-09 15:23 云风
Q:为啥非要主动遍历弱表才能这样呢。。。。

A:呵呵,因为 id 得保留,不然新分配的内存块分配出来的 id 可能和弱表里记录的重复。这样从弱表里就可能取到和当初放进去不同的内存指针了。关键是在于,回收一块内存时,无法反查到内存被哪些弱表引用着。  回复  更多评论
  
# re: 垃圾收集的那点事(K) 2012-06-11 18:16 雨过天晴
一口气把gc这个系列看完了,醍醐灌顶,谢谢LZ了  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理