我也研究下云风的垃圾回收库

在网上闲逛时发现了一个云风写的垃圾回收库和源码学习文档,我也一起研究一下,一方面弥补一下我对gc知识理解的不足,另一方面督促自己把这个不足1000行代码确足够诡异的迷你gc库看完,搞清楚原理。

参考:
源码地址:http://manualgc.googlecode.com/svn/trunk/
另外一位同学写的分析文章:http://www.cppblog.com/darkdestiny/archive/2008/09/10/61528.html

写的挺详细,不过我还是记录一下自己理解的部分

我也从分配内存的gc_malloc函数开始
大家都知道由malloc,realloc,calloc分配内存需要释放,那么一个自动内存管理的库,自然需要重写这3个函数,实际上这个库重写了下面这3个函数

C代码 复制代码
  1. #define my_malloc malloc   
  2. #define my_free free   
  3. #define my_realloc realloc  



具体看malloc函数前先看一下这个函数涉及到的主要的数据结构

C代码 复制代码
  1. struct node {   
  2.     int mark;   
  3.     union {   
  4.         struct {   
  5.             void * mem;   
  6.             struct link *children;   
  7.             void (*finalizer)(void *);   
  8.         } n;   
  9.         struct {   
  10.             intptr_t mem;   
  11.             struct link *children;   
  12.             intptr_t weak;   
  13.         } c;   
  14.         int free;   
  15.     } u;   
  16. };   
  17.   
  18. struct hash_node {   
  19.     int id;   
  20.     struct hash_node *next;   
  21. };   
  22.   
  23. struct hash_map {   
  24.     struct hash_node **table;   
  25.     int size;   
  26.     struct hash_node *free;   
  27.     int number;   
  28. };   
  29.   
  30. static struct {   
  31.     struct node *pool;   
  32.     int size;   
  33.     int free;   
  34.     int mark;   
  35.     bool cache_dirty;   
  36.     struct stack stack;   
  37.     struct hash_map map;   
  38.     struct cache_node cache[CACHE_SIZE];   
  39. } E;  



E是这个库对内存管理的容器,我们今天看的主要是struct node *pool这个一维线性空间和struct hash_map这个hash表

实际上每个被分配的内存块会放入pool这个一维线性空间里,然后通过hash_map记录这个空间的id号,将来通过这个内存块的指针就能直接方便的通过hash找到pool的id,最终得到这个node的信息

看一下malloc是如何被重写的

C代码 复制代码
  1. void*   
  2. gc_malloc(size_t sz,void *parent,void (*finalizer)(void *))   
  3. {   
  4.     void *ret=my_malloc(sz);   
  5.     int id=map_id(ret);   
  6.     E.pool[id].u.n.finalizer=finalizer;   
  7.     if (parent) {   
  8.         gc_link(parent,0,ret);   
  9.     }   
  10.     else {   
  11.         stack_push(id);   
  12.     }   
  13.     return ret;   
  14. }  



my_malloc实际就是malloc,然会调用map_id记录这块内存分配的信息

C代码 复制代码
  1. static int  
  2. map_id(void *p)   
  3. {   
  4.     int h=hash(p);   
  5.     struct hash_node *node=E.map.table[h & (E.map.size -1)];   
  6.     while (node) {   
  7.         if (E.pool[node->id].u.n.mem==p) {   
  8.             return node->id;   
  9.         }   
  10.         node=node->next;   
  11.     }   
  12.     if (E.map.number >= E.map.size) {   
  13.         map_expand();   
  14.     }   
  15.   
  16.     ++E.map.number;   
  17.   
  18.     if (E.map.free) {   
  19.         node=E.map.free;   
  20.         E.map.free=node->next;   
  21.     }   
  22.     else {   
  23.         node=(struct hash_node *)my_malloc(sizeof(*node));   
  24.     }   
  25.     node->id=node_alloc(p);   
  26.     node->next=E.map.table[h & (E.map.size -1)];   
  27.     E.map.table[h & (E.map.size -1)]=node;   
  28.   
  29.     return node->id;   
  30. }  



函数首先根据指针地址计算一个hash值,具体算法我们就没必要细究了
然后根据hash值取出这个hash值对应的链表(这个也是链式hash结构)
接着while循环遍历链表,链表里存放的是这个被分配的内存块在E.pool这个一维线性空间内的id值,所以通过 if (E.pool[node->id].u.n.mem==p) 来查找是否是这个内存块

这里应该是找不到的,那么下面做得事情是:

1.将*p所指向的内存块分配到E.pool这个线性空间内,并得到这个被分配空间的id
2.将这个id号存入hashmap中,方面将来直接在E.pool中定位到这个空间

第一步工作实际执行的是以下代码

node=(struct hash_node *)my_malloc(sizeof(*node));
node->id=node_alloc(p);

第二步执行的是:

node->next=E.map.table[h & (E.map.size -1)];
E.map.table[h & (E.map.size -1)]=node;

再看一下 node_alloc(p)

C代码 复制代码
  1. static int  
  2. node_alloc(void *p)   
  3. {   
  4.     struct node *ret;   
  5.     if (E.free==-1) {   
  6.         int sz=E.size * 2;   
  7.         int i;   
  8.         if (sz==0) {   
  9.             sz=1024;   
  10.         }   
  11.   
  12.         E.pool=(struct node *)my_realloc(E.pool,sz*sizeof(struct node));   
  13.         ret=E.pool + E.size;   
  14.         ret->u.n.children=0;   
  15.   
  16.         for (i=E.size+1;i<sz;i++) {   
  17.             E.pool[i].u.free=i+1;   
  18.             E.pool[i].mark=-1;   
  19.             E.pool[i].u.n.children=0;   
  20.         }   
  21.         E.pool[sz-1].u.free=-1;   
  22.         E.free=E.size+1;   
  23.         E.size=sz;   
  24.     }   
  25.     else {   
  26.         ret=E.pool + E.free;   
  27.         E.free = E.pool[E.free].u.free;   
  28.     }   
  29.     ret->u.n.mem=p;   
  30.     ret->mark=0;   
  31.     ret->u.n.finalizer=0;   
  32.     if (ret->u.n.children) {   
  33.         ret->u.n.children->number=0;   
  34.     }   
  35.     return ret-E.pool;   
  36. }  



通过gc_init函数得知这里执行的是E.free==-1这段分支

E.size初始也是0
所以实际上E.pool这个线性空间是在这时被realloc初始化的

ret=E.pool+e.size 也就是 ret=E.pool+0;即首个元素,下面的for循环是:
for(i=1;i<1024;i++)

即初始化一下这个一维空间的所有元素值
最有意思的是:E.pool[i].u.free=i+1;
就是说:
E.pool[1].u.free=2;
E.pool[2].u.free=3;
...
E.pool[1022].u.free=1023
E.pool[1023].u.free=-1

当前的E.free=1
最后返回的是ret-E.pool,根据前面的ret=E.pool+0,得知此时函数返回值是0
即E.pool这个一维空间是按id递增的顺序分配的

可以试着想象如果是第二次分配空间
E.free==-1条件不满足,(实际E.free=1)

所以执行

ret=E.pool + E.free;
E.free = E.pool[E.free].u.free;

即ret=E.pool+1,返回的ret-E.pool实际就是返回ID为1,可见是顺序递增的

另外可以看到
E.free=E.pool[E.free].u.free
实际上是:E.free=E.pool[1].u.free
而E.pool[1].u.free=2

所以第二次调用该函数后E.free=2

今天先看这么多,写的很乱,有兴趣的还是自己看源码吧

 

 

文章转自:http://www.javaeye.com/topic/352733

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值