This time to try to achieve a simple C-language garbage collector, the code is not much, but for my experience, it really cost a lot of thought.
Thank you for Yunfeng's open Source: Cloud Wind: blog.codingnow.com/2008/06/gc_for_c.html
and LOGOS. A conceptual version of YFGC's interpretation and implementation: Logos:www.cppblog.com/darkdestiny
I know there are a lot of things to think about in my own realization. But still decided to stick it out, tell me the idea of realization, there are deficiencies in the local people point out to correct.
We know that garbage collection technology is mainly divided into the following, they have their own advantages and disadvantages:
1. Citation technology (Reference counting)
2. Mark Clear (Mark-sweep)
3. Mark Finishing/tightening (mark-compact)
4. Node replication (Copying)
5. Collection of generational (generational collecting)
I'm not going to go into a variety of ideas here, Google has. My requirement is: Require garbage collection can naturally solve the problem of circular reference, occupy not too much space, lightweight implementation, solve the problem of memory fragmentation.
Here I am using the tag cleanup algorithm, memory allocation uses the memory pool.
The first is to give the interface of the memory pool, implementation in my previous article has said, do not post it out. The implementation of the memory pool draws on the SGI STL's implementation approach. Code: #ifndef _mem_pool_h #define _MEM_POOL_H #include <stddef.h> void *mem_malloc (size_t n); void Mem_free (void *p, size_t n); void *mem_realloc (void* ptr, size_t NEW_SZ, size_t OLD_SZ); #endif
then look at the GC interface: Code: #ifndef garbage_collector_h #define garbage_ Collector_h #include <stddef.h> #define my_ Malloc mem_malloc #define my_free mem_free #define my_realloc Mem_realloc /* define the level of node */ # Define node_root_level 0 /* the node is global Variable or in main () */ #define node_now_level 1 /* the node is just use in present fucntion */ #define node_pre_level 2 /* the node is be referenced by call function */ void gc_init (); Void gc_exit (); Void func_end (void *p, ..); Void gc_link (Void *p, int level); /* gc will mark the memory in gc_enter/gc_leave for collect later */ Void gc_enter (); Void gc_leave (); void gc_collect (); Void *gc_malloc (size_t sz, void (*finalizer) (void *)); Void *gc_realloc (VOID&NBSP;*P,&NBSP;SIZE_T&NBSP;SZ); #endif
data structures used: code:struct node { int mark; /* mark for gc collect */ int level; /* the node leavel */ struct { void *mem; /* the pointer point to memory*/ int size; /* the size of memrory */ void (*finalizer) (void *); /* destruction when the mem be free */ }n; }; static struct { struct node *pool; /* an array for store nodes */ int size; /* the size of pool */ int free; /* the next free node in pool */ Struct stack *stack; /* the stack used for store Pointer in fuction */ } e;
The level values here are: Node_root_level, Node_now_level, Node_pre_level. Based on this consideration, we know that allocating a chunk of memory dynamically, if you want to extend its lifetime, is either passed back through the function return value, either through a multilevel pointer, or directly to a global variable. So this GC is based on a strategy where the node level value of the user-allocated memory block is initialized to Node_now_level, and if the user needs to extend its lifetime to the previous function or global variable, call Gc_link and pass in the corresponding level value. It is only necessary to call Func_end at the end of the function, when passing dynamic memory through the return value, only when its lifetime needs to be extended to the upper-level function. The Func_end function is to set the level value of the memory block to Node_now_level.
Knowing the life of the end point, the mark is simple. Gc_leave is responsible for EVEL the nodes in the current function stack as node_now_level to mark_collect, thus recovering them in gc_collect. What you need to say here is that the memory allocated in the main () function and the memory that is attached to the global variable are freed in Gc_exit.
the general process to know, the following is the implementation: Code: #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <assert.h> #include "stack.h" #include "mem_pool.h" #include "gc.h" #define POOL_INITIAL_NUMBER 1024 /* the Initial number of nodes in pool */ #define pool_max_number 4096 /* the Max Number of nodes in pool */ #define STACK_SECTION_TAG NULL /* the tag to section the stack */ #define MARK_INITIAL -1 /* the node initialed */ #define mark_reserve 0 /* the node marked for reserve */ #define mark_collect 1 /* the node marked for collect */ struct node { int mark; /* mark for gc collect */ int level; /* the node leavel */ struct { void *mem; /* the pointer point to memory*/ int size; /* the size of memrory */ void (*finalizer) (void *); /* destruction when the mem be free */ }n; }; static struct { struct node * pool; /* an array for store the pointer of node */ int size; /* the size of pool */ int Free; /* the next free node in pool */ struct stack *stack; /* the stack used for store Pointer in fuction */ } E; Static bool pool_Compact () { int i, j; struct node temp; for (i = 0; i < e.free; i++) { if ( e.pool[i].mark == mark_initial) { temp = E.pool[i]; for (j = e.free; j > i; j--) { if (e.pool[j].mark != mark_initial) { e.pool[i ] = e.pool[j]; &nBsp E.pool[j] = temp; break; } } } } for (i = 0; i < e.size; i++) { if (e.pool[i].mark == mark_initial) { E.free = I break; } } return e.free >= e.size ? true : false; } static void node_init () { int i; for (i = e.free; i < e.size; i++) { e.pool[i]. mark = mark_initial; E.pool[i].level = NODE_NOW_LEVEL; E.pool[i].n.mem = NULL; e.pool[i].n.finalizer =  NULL; } } Static void pool_expand () & nbsp; { int expand_size; bool expand = false; expand_size = e.size * 2; if (expand_size >= pool_max_number * sizeof ( Struct node)) { expand = Pool_compact (); } if (expand) { E.pool = ( struct node *) My_realloc (e.pool, expand_size * sizeof (struct node), &nBsp; e.size * sizeof (Struct node)); E.free = E.size; E.size = expand_size; /* init the node */ node_init (); } } static void node_alloc (void *p, size_t sz, void (*finalizer) (void *)) { if (e.free >= e.size) { pool_expand (); } E.pool[E.free].mark = MARK_RESERVE; E.pool[E.free].level = NODE_NOW_LEVEL; E.pool[E.free].n.mem = p; e.pool[e.free].n.size = sz; // for mem_free E.pool[E.free].n.finalizer = finalizer; E.free++; } static void Pool_init () { E.pool = (struct node *) my_ malloc (pool_initial_number * sizeof (Struct node)); E.free = 0; E.size = POOL_INITIAL_NUMBER; /* init the node * / &Nbsp; node_init (); } void gc_init () { E.pool = NULL; E.size = 0; E.free = -1; e.stack = init_stack (); pool_init (); } Void gc_link (void *p, int level) { int i; for (i = 0; i < e.free; i++) { if ( e.pool[i].n.mem == p) { e.pool[i].level = level; break; } } } Void gc_enter () { push (e.stack, stack_section_tag); } /* accordind to the level of nodes, mark nodes. if in present stack section * of function , there are some nodes ' life extend father function ' s life * which callthe present function, then push these nodes in stack section * of father ' s function. */ void gc_leave () { void *p; struct stack *stack_temp; stack_temp = init_stack (); while ((P = top (e.stack)) != stack_section_tag) { int i; /* whether mark for gc collect or not by searching for the node &