posts - 18,  comments - 104,  trackbacks - 0
上篇说到垃圾回收器的一些基本原理。这次说说这个垃圾回收器最终效果啥呀。有着明确的需求,说起来会更清晰,更明确。

看代码吧。

struct A
{
    int i,j,k;

    Begin_Member_Pointer(A)
    End_Member_Pointer

    
};


struct B
{
    pointer<A> data0;

    B()
    {
        data0 = gc.malloc<A>();
    }

    Begin_Member_Pointer(B)
    Member(data0);
    End_Member_Pointer

};

pointer<B> test()
{
    pointer<B> pB = gc.malloc<B>();
    //做一些事情
    return pB;
}

void main()
{
    pointer<B> pB = test();
    //做一些事情
    gc.collect();
    gc.compress();

}

看看上面的一段代码,基本演示了怎么用这个垃圾回收器。

1.  gc.malloc< >(  ) 为对象分配空间,并调用构造函数。gc.malloc有多个参数的模板函数,构造函数的参数直接放里面就可以。
2.  能被垃圾回收器回收的对象,必须定义一个以Begin_Member_Pointer(TypeName) 开始,以End_Member_Pointer结束,中间包含所有pointer类型成员变量的列表(顺序无关)。
3.  在任意时候调用gc.collect(  )来回收所有无用内存,并会对所有被回收的对象调用析构函数。
4.  在回收后可以选择调用gc.compress(  )来紧缩内存。

当然现在如果有朋友读过源码,会发现和上面所有有些不一致,不过上面是最终的效果。

这里就对指针的使用有了一些限制,

1. 所有受垃圾回收器控制的内存的指针是以 pointer<type> 的形式存在,而且是强制的。pointer类型不能转化成普通指针,而且gc.malloc函数返回的也是pointer类型的指针,这个是必须的,因为要实现内存紧缩,所有的指针必须受控)。
2. 所有的类型定义里面必须包含对成员指针的一个描述,见上面第2条。否则想上面在B的构造函数中申请出来的A对象就无法被释放了。

这些对成员的描述就是上面图中粉红色圆圈的指针了,如果没有这些定义,有些内存块的可达性判断就可能失效。

这里我做了一些简化,暂时不考虑多核多线程,所以寄存器可以不考虑,这大幅降低了编码的复杂度,使得代码更容易理解。

先看看所有的数据结构吧:

//扫描成员指针的函数指针
typedef void (*marker) (const void* ptr, void(*func)(const pointer_base&));

//根集
std::list<pointer_base *> gc_root_set;

//内存块节点
struct node
{
    
void* mem;        //内存指针(普通指针)
    unsigned int size;    //内存大小
    unsigned int mark;    //当前的标志
    marker func;        //扫描函数指针
    destructor finalizer;    //析构函数指针
}
;

//内存块集
std::map<const void *, node> gc_holder;
//标志
unsigned int gc_marker = 0;
//标志当前指针是否属于根集
bool gc_is_root_set_locked = false;
//所有指针集(用于紧缩)
std::multimap<const void*const pointer_base*> gc_pointer_set;
//紧缩时用的缓冲
std::map<const void*const void*> gc_swap_buffer;

上面就是全部的数据结构,需要说明的是:
1。 那个marker是一个函数指针,它的第二个参数也是个函数指针。
2。 了解C++的朋友可能会说,析构函数不能对其取地址。当然这里用了一个小小的技巧---对析构函数进行了包装。

template <class _T>
void gc_destructor(_T* p)
{
    p
->_T::~_T();
}
;

typedef 
void (*destructor) (void*);

struct node
{
    
void* mem;
    unsigned 
int size;
    unsigned 
int mark;
    marker func;
    
//注意类型
    destructor finalizer;
}
;

node n;
//这是关键
n.finalizer = (destructor)&gc_destructor<_T>;

 

3。 注意gc_mark不是一个bool变量,它会随着每次分配而++,在collect之前,会用++mark来标记所有可达内存块,即node.mark = gc_mark; 于是,所有mark值和gc_mark不等的内存块就需要被回收啦。

从下篇开始就逐个介绍各个函数了,最后再用宏或模板做出来一些语法糖,把见不得人的东西都包起来,就大功告成了。

posted on 2010-02-10 19:09 尹东斐 阅读(2584) 评论(10)  编辑 收藏 引用

FeedBack:
# re: C++下垃圾回收器的实现(二)--析构函数的地址?
2010-02-10 19:54 | jimmy
果然大牛啊!!  回复  更多评论
  
# re: C++下垃圾回收器的实现(二)--析构函数的地址?
2010-02-12 12:23 | Benjamin
如果能考虑一下异常,会完整些  回复  更多评论
  
# re: C++下垃圾回收器的实现(二)--析构函数的地址?
2010-02-13 12:00 | yindf
@Benjamin

完成了会考虑的,谢谢支持。  回复  更多评论
  
# re: C++下垃圾回收器的实现(二)--析构函数的地址?
2010-02-18 12:04 | 路过
失败的垃圾设计,买本《垃圾收集》来看看好不?  回复  更多评论
  
# re: C++下垃圾回收器的实现(二)--析构函数的地址?
2010-02-18 20:01 | yindf
@路过

呵呵~~你看过的话在C++下实现一个吧,我求你了。  回复  更多评论
  
# re: C++下垃圾回收器的实现(二)--析构函数的地址?
2010-02-18 21:53 | 路过
《Garbage Collection》Jones, Richard/ Lins, Rafael著,
强烈建议仔细读读这本书,去看看HP CPPGC源码。概念不清,常识性错误。
唉。。。  回复  更多评论
  
# re: C++下垃圾回收器的实现(二)--析构函数的地址?
2010-02-19 02:25 | yindf
@路过

HP gc看过啦,我在上篇提到过,HP gc有它的缺点。
C++不能完全控制指针,所以HP gc说过,它是“保守的”垃圾回收。

而我这个,就是精确的。  回复  更多评论
  
# re: C++下垃圾回收器的实现(二)--析构函数的地址?
2010-02-20 00:15 | 路过
精确的?!Are you kidding?!  回复  更多评论
  
# re: C++下垃圾回收器的实现(二)--析构函数的地址?
2010-02-20 22:41 | yindf
@路过

呵呵,只要是用gc malloc 分配出来的,都可以保证在不用的时候,通过collect收集。 HP gc不行。  回复  更多评论
  
# re: C++下垃圾回收器的实现(二)--析构函数的地址?
2010-04-06 09:30 | 不知所谓
楼主你去看看楼上说的那本书好不咯?你这个所谓gc简直就是幼稚得搞笑  回复  更多评论
  

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


<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

常用链接

留言簿(4)

随笔档案

文章分类

文章档案

相册

好友博客

搜索

  •  

最新评论

阅读排行榜

评论排行榜