现在的位置: 首页 > 综合 > 正文

简易的c垃圾收集器 一个简易的c垃圾收集器

2017年09月30日 ⁄ 综合 ⁄ 共 11357字 ⁄ 字号 评论关闭

一个简易的c垃圾收集器

 262人阅读 评论(9) 收藏 举报

      这段时间试着实现了一个简易的C语言垃圾收集器,代码不多,但对我的经验而言,确实费了不少心思。
      这里感谢云风兄的开源:云风:blog.codingnow.com/2008/06/gc_for_c.html
      以及LOGOS兄的对yfgc的解析和实现的一个概念版本:LOGOS:www.cppblog.com/darkdestiny
      话说我知道自己的实现里面,有很多考虑不足的地方。但还是决定贴出来,讲述的我实现的思想,有不足的地方大家指出来也好纠正。
       大家知道垃圾收集技术主要分为以下几种,他们有各自的优点与缺点:
       1、引用技术(Reference Counting)
       2、标记清除(Mark-Sweep)
       3、标记整理/紧缩(Mark-Compact)
       4、结点复制(Copying)
       5、分代收集(Generational Collecting)
       这里我就不详述各种思想了,google上有。我的需求是:要求垃圾收集能自然的解决循环引用的问题,占用的空间不要太多,轻量级的实现,解决内存碎片的问题。
       这里我选用的是标记清除算法,内存分配使用内存池。
       首先还是给出内存池的接口,实现在我之前的文章中有讲,就不贴出来了。内存池的实现借鉴了sgi stl的实现思路。

Code:
  1. #ifndef _MEM_POOL_H   
  2. #define _MEM_POOL_H   
  3.   
  4. #include <stddef.h>   
  5.   
  6. void *mem_malloc(size_t n);
      
  7. void mem_free(void *p, size_t n);
      
  8. void *mem_realloc(void* ptr, size_t new_sz, size_t old_sz);
      
  9.   
  10. #endif   

    然后看gc的接口:

Code:
  1. #ifndef GARBAGE_COLLECTOR_H   
  2. #define GARBAGE_COLLECTOR_H   
  3.   
  4. #include <stddef.h>   
  5.   
  6. #define my_malloc mem_malloc   
  7. #define my_free mem_free   
  8. #define my_realloc mem_realloc   
  9.   
  10. /* define the level of node */  
  11. #define NODE_ROOT_LEVEL 0 /* the node is global
    variable or in main() */ 
      
  12. #define NODE_NOW_LEVEL 1  /* the node is just use in  present fucntion */   
  13. #define NODE_PRE_LEVEL 2  /* the node is be referenced by call function */   
  14.   
  15.   
  16. void gc_init();
      
  17. void gc_exit();
      
  18.   
  19. void func_end(void *p, ...);
      
  20. void gc_link(void *p, int level);
      
  21.   
  22. /* gc will mark the memory in gc_enter/gc_leave for collect later */  
  23. void gc_enter();
      
  24. void gc_leave();
      
  25.   
  26. void gc_collect();
      
  27.   
  28. void *gc_malloc(size_t sz, void (*finalizer)(void *));
      
  29. void *gc_realloc(void *p, size_t sz);
      
  30.   
  31. #endif   

  用到的数据结构:

Code:
  1. struct node {
      
  2.     int mark;          /* mark for gc collect */  
  3.     int level;         /* the node leavel */  
  4.     struct {
      
  5.         void *mem;     /* the pointer point to memory*/  
  6.         int size;      /* the size of memrory */  
  7.         void (*finalizer)(void *); /* destruction when the mem be free */  
  8.     }n;
      
  9. };
      
  10.   
  11. static struct {
      
  12.     struct node *pool;   /* an array for store
    nodes */
      
  13.     int size;            /* the size of pool */  
  14.     int free;            /* the next free node in pool */  
  15.     struct stack *stack; /* the stack used for store
    pointer in fuction */
      
  16. } E;  

      这里 level 取值为:NODE_ROOT_LEVEL、NODE_NOW_LEVEL、NODE_PRE_LEVEL。 基于这样的考虑:我们知道动态分配一块内存,如果要延长其生命期,要么通过函数返回值传回,要么通过多级指针,或者直接挂到全局变量上。所以这个gc基于这样的策略:首先用户分配的内存块所在的结点 level 值初始化为NODE_NOW_LEVEL,如果用户需要延长其生命期到上一级函数或全局变量,那么调用 gc_link 并传入相应的 level 值。仅在其生命期需要延长至上一级函数时需要在函数结尾处(通过返回值传递动态内存时需要在
return 前)调用 func_end。func_end的作用是将该内存块的 level 值设置为NODE_NOW_LEVEL。
      知道了结点的生命期,标记就简单了。gc_leave负责将当前函数栈中的evel为NODE_NOW_LEVEL的结点标记为MARK_COLLECT,从而在 gc_collect 中回收。这里需要说的是main()函数中分配的内存和挂到全局变量的内存会在gc_exit中释放。
      大致过程知道了,下面就是具体实现了:

Code:
  1. #include <stdio.h>   
  2. #include <stdlib.h>   
  3. #include <stdarg.h>   
  4. #include <assert.h>   
  5. #include "stack.h"   
  6. #include "mem_pool.h"   
  7. #include "gc.h"   
  8.   
  9.   
  10.   
  11. #define POOL_INITIAL_NUMBER 1024 /* the initial number of nodes
    in pool */ 
      
  12. #define POOL_MAX_NUMBER 4096     /* the
    max number of nodes in pool */ 
      
  13.   
  14. #define STACK_SECTION_TAG NULL /* the tag to section the stack */   
  15.   
  16. #define MARK_INITIAL -1     /* the node initialed */   
  17. #define MARK_RESERVE 0      /* the node marked for reserve */   
  18. #define MARK_COLLECT 1      /* the node marked for collect */   
  19.   
  20. struct node {
      
  21.     int mark;          /* mark for gc collect */  
  22.     int level;         /* the node leavel */  
  23.     struct {
      
  24.         void *mem;     /* the pointer point to memory*/  
  25.         int size;      /* the size of memrory */  
  26.         void (*finalizer)(void *); /* destruction when the mem be free */  
  27.     }n;
      
  28. };
      
  29.   
  30. static struct {
      
  31.     struct node *pool;   /* an array for store the pointer of node */  
  32.     int size;            /* the size of pool */  
  33.     int free;            /* the next free node in pool */  
  34.     struct stack *stack; /* the stack used for store
    pointer in fuction */
      
  35. } E;
      
  36.   
  37. static bool pool_compact()
      
  38. {
      
  39.     int i, j;
      
  40.     struct node temp;
      
  41.     
      
  42.     for (i = 0; i < E.free; i++) {
      
  43.         if (E.pool[i].mark == MARK_INITIAL) {
      
  44.             temp = E.pool[i];
  45.             for (j =
    E.free; j > i; j--) {   
  46.                 if (E.pool[j].mark != MARK_INITIAL) {
      
  47.                     E.pool[i] = E.pool[j];
      
  48.                     E.pool[j] = temp;
      
  49.                     break;
      
  50.                 }
      
  51.             }
      
  52.         }
      
  53.     }
      
  54.     
      
  55.     for (i = 0; i < E.size; i++) {
      
  56.         if (E.pool[i].mark == MARK_INITIAL) {
      
  57.             E.free = i;
      
  58.             break;
      
  59.         }
      
  60.     }
      
  61.     
      
  62.     return E.free >= E.size ? true : false;
      
  63. }
      
  64.   
  65. static void node_init()
      
  66. {
      
  67.     int i;
      
  68.     
      
  69.     for (i = E.free; i < E.size; i++) {
      
  70.         E.pool[i].mark = MARK_INITIAL;
      
  71.         E.pool[i].level = NODE_NOW_LEVEL;
      
  72.         E.pool[i].n.mem = NULL;
      
  73.         E.pool[i].n.finalizer = NULL;
      
  74.     }
      
  75. }
      
  76.   
  77. static void pool_expand()
      
  78. {
      
  79.     int expand_size;
      
  80.     bool expand = false;
      
  81.     
      
  82.     expand_size = E.size * 2;
      
  83.     if (expand_size >= POOL_MAX_NUMBER * sizeof(struct node)) {
      
  84.          expand = pool_compact();
      
  85.     } 
      
  86.     
      
  87.     if (expand) {
      
  88.         E.pool = (struct node *)my_realloc(E.pool, expand_size * sizeof(struct node),
      
  89.              E.size * sizeof(struct node));
      
  90.         E.free = E.size;
      
  91.         E.size = expand_size;
      
  92.         
      
  93.         /* init the node */  
  94.         node_init();
      
  95.     }   
      
  96. }
      
  97.   
  98. static void node_alloc(void *p, size_t sz, void (*finalizer)(void *))
      
  99. {
      
  100.     if (E.free >= E.size) {
      
  101.         pool_expand();
      
  102.     }
  103.   
     E.pool[E.free].mark = MARK_RESERVE;   
  104.   
     E.pool[E.free].level = NODE_NOW_LEVEL;   
  105.   
     E.pool[E.free].n.mem = p;   
  106.   
     E.pool[E.free].n.size = sz; 
    // for mem_free   
  107.   
     E.pool[E.free].n.finalizer = finalizer;   
  108.         
      
  109.   
     E.free++;   
      
       
  110. }
      
  111.   
  112.   
  113.   
  114. static void pool_init()
      
  115. {
      
  116.     E.pool = (struct node *)my_malloc(POOL_INITIAL_NUMBER * sizeof(struct node));
      
  117.     E.free = 0;
      
  118.     E.size = POOL_INITIAL_NUMBER;
      
  119.     
      
  120.     /* init the node */  
  121.     node_init();
      
  122. }
      
  123.   
  124.   
  125.   
  126. void gc_init()
      
  127. {
      
  128.     E.pool = NULL;
      
  129.     E.size = 0;
      
  130.     E.free = -1;
      
  131.     E.stack = init_stack();
      
  132.     
      
  133.     pool_init();
      
  134. }
      
  135.   
  136. void gc_link(void *p, int level)
      
  137. {
      
  138.     int i;
      
  139.     
      
  140.     for (i = 0; i < E.free; i++) {
      
  141.         if (E.pool[i].n.mem == p) {
      
  142.             E.pool[i].level = level;
      
  143.             break;
      
  144.         }
      
  145.     }
      
  146. }
      
  147.   
  148. void gc_enter()
      
  149. {
      
  150.     push(E.stack, STACK_SECTION_TAG);
      
  151. }
      
  152.   
  153. /* accordind to the level of nodes, mark nodes. if in present stack section   
  154.  * of function, there are some nodes' life extend father function's life   
  155.  * which callthe present function, then push these nodes in stack section  
  156.  * of father's function.  
  157.  */  
  158.   
  159. void gc_leave()
      
  160. {
      
  161.     void *p;
      
  162.     struct stack *stack_temp;
      
  163.     
      
  164.     stack_temp = init_stack();
      
  165.     while ((p = top(E.stack)) != STACK_SECTION_TAG) {
      
  166.         int i;
      
  167.         
      
  168.         /* whether mark for gc collect or not by searching for the node   
  169.             whose mem element equals p. */  
  170.         for (i = 0; i < E.free; i++) {
      
  171.             if (E.pool[i].n.mem == p ) {
      
  172.                 if (E.pool[i].level == NODE_NOW_LEVEL) {
      
  173.                     E.pool[i].mark =  MARK_COLLECT;
      
  174.                 } else if (E.pool[i].level == NODE_PRE_LEVEL) {
      
  175.                     push(stack_temp, p);
      
  176.                 }
      
  177.                 break;
      
  178.             }
      
  179.         }
      
  180.         
      
  181.         pop(E.stack);
      
  182.     }
      
  183.     
      
  184.     pop(E.stack); /* pop the STACK_SECTION_TAG */  
  185.     
      
  186.     while (! stack_empty(stack_temp)) {
      
  187.         p = top(stack_temp);
      
  188.         push(E.stack, p);
      
  189.         pop(stack_temp);
      
  190.     }
      
  191.     
      
  192.     destory_stack(stack_temp);
      
  193. }
      
  194.   
  195. void gc_collect()
      
  196. {
      
  197.     int i;
      
  198.     
      
  199.     for (i = 0; i < E.free; i++) {
      
  200.         if (E.pool[i].mark == MARK_COLLECT) {
      
  201.             void *p = E.pool[i].n.mem;
      
  202.             int sz = E.pool[i].n.size;
      
  203.             if (E.pool[i].n.finalizer != NULL) {
      
  204.                 E.pool[i].n.finalizer(p);
      
  205.             }
      
  206.             my_free(p, sz); // for mem_free(p, size);   
  207.             
      
  208.             E.pool[i].mark = MARK_INITIAL;
      
  209.         }
      
  210.     }
      
  211. }
      
  212.   
  213. void *gc_malloc(size_t sz, void (* finalizer)(void *))
      
  214. {
      
  215.     void *result = my_malloc(sz);
      
  216.     node_alloc(result, sz, finalizer);
      
  217.     push(E.stack, result);
      
  218.     return result;
      
  219. }
      
  220.   
  221. void *gc_realloc(void *p, size_t sz)
      
  222. {
      
  223.     void *result;
      
  224.     assert(sz > 0);
      
  225.     
      
  226.     if (p == NULL) {
      
  227.         return gc_malloc(sz, NULL);
      
  228.     }
      
  229.     
      
  230.     /* find the node contain p */  
  231.     int i;
      
  232.     int old_sz;
      
  233.                 
      
  234.     for (i = 0; i < E.free; i++) {
      
  235.         if (E.pool[i].n.mem == p) {
      
  236.             old_sz = E.pool[i].n.size;
      
  237.             break;
      
  238.         }
      
  239.     }
      
  240.   
  241.     result = my_realloc(p, sz, old_sz);
      
  242.     /* if new memory address is not change, just update size and return */  
  243.     if (result == p) {  
      
  244.         E.pool[i].n.size = sz;
      
  245.         return result;
      
  246.     } else {
      
  247.         /* update size and mem */  
  248.         E.pool[i].n.size = sz;
      
  249.         E.pool[i].n.mem = result;
      
  250.         
      
  251.         /* update the stack infomation */  
  252.         
      
  253.         void *temp;
      
  254.         struct stack *stack_temp = init_stack();
      
  255.         
      
  256.         while ((! stack_empty(E.stack)) && ((temp = top(E.stack)) != p)) {
      
  257.             push(stack_temp, temp);
      
  258.             pop(E.stack);
      
  259.         }
      
  260.         
      
  261.         /* if the stack is not empty, pop the old address p and push   
  262.             the new address result */  
  263.         
      
  264.         if (! stack_empty(E.stack)) {
      
  265.             pop(E.stack);
      
  266.             push(E.stack, result);
      
  267.         }
      
  268.         
      
  269.         /* push the former data */  
  270.         while (! stack_empty(stack_temp)) {
      
  271.             temp = top(stack_temp);
      
  272.             push(E.stack, temp);
      
  273.             pop(stack_temp);
      
  274.         }
      
  275.         
      
  276.         destory_stack(stack_temp);
      
  277.     }
      
  278.     return result;
      
  279. }
      
  280.   
  281. void gc_exit()
      
  282. {
      
  283.     /* the rest nodes (normally are root nodes) will free in this function */  
  284.     int i;
      
  285.     
      
  286.     for (i = 0; i < E.free; i++) {
      
  287.         if (E.pool[i].mark != MARK_INITIAL) {
      
  288.             E.pool[i].mark = MARK_COLLECT;
      
  289.         }
      
  290.     }
      
  291.     
      
  292.     gc_collect();
      
  293.     
      
  294.     my_free(E.pool, E.size * sizeof(struct node));
      
  295.     
      
  296.     destory_stack(E.stack);
      
  297. }
      
  298.   
  299. /* this remand the last paramater must be NULL */  
  300. void func_end(void *p, ...)
      
  301. {
      
  302.     va_list ap;
      
  303.     void *temp = p;
      
  304.     va_start(ap, p);
      
  305.     
      
  306.     int i;
      
  307.     while (temp != NULL) {
      
  308.         for (i = 0; i < E.free; i++) {
      
  309.             if (E.pool[i].n.mem == temp) {
      
  310.                 E.pool[i].level = NODE_NOW_LEVEL;
      
  311.                 break;
      
  312.             }
      
  313.         }
      
  314.         
      
  315.         temp = va_arg(ap, void *);
      
  316.     }
      
  317.     
      
  318.     va_end(ap);
      
  319. }
      

这里给出测试代码:

Code:
  1. #include <stdio.h>   
  2. #include "gc.h"   
  3.   
  4. static void log_ptr(void *p)
      
  5. {
      
  6.     printf("free %p/n",p);
      
  7. }
      
  8.   
  9. int *foo1()
      
  10. {
      
  11.     printf("in function foo1:/n");
      
  12.     int *p3 = gc_malloc(3, log_ptr);
      
  13.     int *p4 = gc_malloc(6, log_ptr);
      
  14.     int *p5 = gc_realloc(p4, 1024);
      
  15.     
      
  16.     gc_link(p3, NODE_PRE_LEVEL);
      
  17.     func_end(p3, NULL);
      
  18.     return p3;
      
  19. }
      
  20.   
  21.   
  22. void foo()
      
  23. {
      
  24.     gc_enter();
      
  25.     int *p1 = foo1();
      
  26.     int *p2 = gc_malloc(4, log_ptr);
      
  27.     
      
  28.     gc_link(p2, NODE_ROOT_LEVEL);
      
  29.     
      
  30.     gc_leave();
      
  31.     
      
  32.     printf("in foo:/n");
      
  33.     gc_collect();
      
  34. }
      
  35.   
  36.   
  37. int main()
      
  38. {
      
  39.     gc_init();
      
  40.     
      
  41.     gc_enter();
      
  42.     
      
  43.     void *p = gc_malloc(5, log_ptr);
      
  44.     foo();
      
  45.     
      
  46.     gc_leave();
      
  47.     
      
  48.     printf("in fuction main:/n");
      
  49.     gc_collect();
      
  50.     gc_exit();
      
  51.     
      
  52.     return 0;
      
  53. }
      

运行结果:

抱歉!评论已关闭.