`
bachmozart
  • 浏览: 111357 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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

阅读更多
在网上闲逛时发现了一个云风写的垃圾回收库和源码学习文档,我也一起研究一下,一方面弥补一下我对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个函数

#define my_malloc malloc
#define my_free free
#define my_realloc realloc


具体看malloc函数前先看一下这个函数涉及到的主要的数据结构
struct node {
	int mark;
	union {
		struct {
			void * mem;
			struct link *children;
			void (*finalizer)(void *);
		} n;
		struct {
			intptr_t mem;
			struct link *children;
			intptr_t weak;
		} c;
		int free;
	} u;
};

struct hash_node {
	int id;
	struct hash_node *next;
};

struct hash_map	{
	struct hash_node **table;
	int size;
	struct hash_node *free;
	int number;
};

static struct {
	struct node *pool;
	int size;
	int free;
	int mark;
	bool cache_dirty;
	struct stack stack;
	struct hash_map map;
	struct cache_node cache[CACHE_SIZE];
} E;


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

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

看一下malloc是如何被重写的

void*
gc_malloc(size_t sz,void *parent,void (*finalizer)(void *))
{
	void *ret=my_malloc(sz);
	int id=map_id(ret);
	E.pool[id].u.n.finalizer=finalizer;
	if (parent) {
		gc_link(parent,0,ret);
	}
	else {
		stack_push(id);
	}
	return ret;
}


my_malloc实际就是malloc,然会调用map_id记录这块内存分配的信息
static int
map_id(void *p)
{
	int h=hash(p);
	struct hash_node *node=E.map.table[h & (E.map.size -1)];
	while (node) {
		if (E.pool[node->id].u.n.mem==p) {
			return node->id;
		}
		node=node->next;
	}
	if (E.map.number >= E.map.size) {
		map_expand();
	}

	++E.map.number;

	if (E.map.free) {
		node=E.map.free;
		E.map.free=node->next;
	}
	else {
		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;

	return node->id;
}



函数首先根据指针地址计算一个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)

static int
node_alloc(void *p)
{
	struct node *ret;
	if (E.free==-1) {
		int sz=E.size * 2;
		int i;
		if (sz==0) {
			sz=1024;
		}

		E.pool=(struct node *)my_realloc(E.pool,sz*sizeof(struct node));
		ret=E.pool + E.size;
		ret->u.n.children=0;

		for (i=E.size+1;i<sz;i++) {
			E.pool[i].u.free=i+1;
			E.pool[i].mark=-1;
			E.pool[i].u.n.children=0;
		}
		E.pool[sz-1].u.free=-1;
		E.free=E.size+1;
		E.size=sz;
	}
	else {
		ret=E.pool + E.free;
		E.free = E.pool[E.free].u.free;
	}
	ret->u.n.mem=p;
	ret->mark=0;
	ret->u.n.finalizer=0;
	if (ret->u.n.children) {
		ret->u.n.children->number=0;
	}
	return ret-E.pool;
}


通过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

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



分享到:
评论
3 楼 crackcell 2009-10-17  
收获颇丰,呵呵~
2 楼 bachmozart 2009-03-22  
mathgl 写道
线程安全么?


这个库没有考虑多线程的情况,就是一个标记清除算法的gc库,也不支持并行收集
具体用法在test.c里面有,为了保证垃圾收集的效率,作者在这个库的实现上做了很大的优化,大部分操作复杂性都是O(1)的,我主要也是想了解这部分
1 楼 mathgl 2009-03-22  
线程安全么?

相关推荐

    云风写的垃圾回收库

    《云风的垃圾回收库与源码学习》 在计算机科学中,内存管理是一项至关重要的任务,尤其是在使用C语言这样的低级编程语言时。云风所编写的垃圾回收库为开发者提供了一种自动管理内存的方法,以防止内存泄漏和不正确...

    云风pbc 库 示例

    云风PBC库是一个针对Protocol Buffers(protobuf)编解码的C语言实现,由知名程序员“云风”开发。Protocol Buffers是Google...通过深入研究这些文件和执行相应的命令,我们可以更好地理解和应用protobuf和云风PBC库。

    游戏之旅-我的编程感悟云风

    游戏之旅-我的编程感悟,云风大神的编程感悟,不知道云风是谁吗???

    lua源码鉴赏云风

    3. **垃圾回收**:Lua使用了简单的引用计数和周期检测相结合的垃圾回收机制。书中会探讨如何有效地跟踪和回收不再使用的对象,以保持内存的高效使用。 4. **内存管理**:Lua如何进行内存分配和释放,包括对小对象的...

    云风的 BLOG- 采访 Lua 发明人的一篇文章

    5. **垃圾回收**:Lua 自带了自动垃圾回收机制,简化了内存管理,降低了程序员的负担。 6. **元表和元方法**:Lua 提供元表概念,允许自定义操作符的行为,这在实现面向对象编程时非常有用。 7. **闭包**:Lua ...

    lua 源码鉴赏 readinglua 云风.zip

    他还详细分析了 Lua 的垃圾回收算法,让读者理解了 Lua 如何高效地管理内存,保持程序运行的流畅性。 此外,文档还涵盖了 Lua 的模块系统和加载机制,以及如何扩展 Lua 的标准库,为开发者提供了自定义功能的途径。...

    云风-lua源码欣赏-lua-5.21

    此外,还涉及到了内嵌库的实现,这些库是Lua功能的重要组成部分,如数学运算、表操作等。 在内存管理章节,作者详细讲解了Lua如何进行内存分配和释放,以及全局状态机的角色。全局状态机是Lua实现多线程安全的关键...

    云风 游戏之旅-我的编程感悟

    虽然给出的部分内容并未包含实际的文字内容,但从标题“云风 游戏之旅-我的编程感悟”以及描述“游戏开发大神云风的经典著作 《游戏之旅-我的编程感悟》”中可以推测出书中可能涉及的关键知识点。 ### 关键知识点...

    云风伙伴算法代码

    回收的时候,如果跟它配对的块也是未被使用的,就合并成一个大的块。标准算法下,分配和释放的时间复杂度都是 O(log N) ,N 不会特别大。算法的优点是碎片率很小。而且很容易做成非入侵式的,不用在被管理的内存上...

    游戏之旅-我的编程感悟(云风著).pdf.part1.rar

    游戏之旅-我的编程感悟(云风编).pdf.part1.rar 本人发布,必是精品。 太简单的我不发。 太易找的我不发。 太经典的我定发。

    云风的Ejoy2D-Engine

    Ejoy2D是由著名游戏开发者云风开发的一款2D游戏引擎,它专为高效、轻量级的游戏开发设计。云风在游戏业界有着丰富的经验,他的作品以其高性能和易用性而闻名。Ejoy2D引擎是他在多年游戏开发实践中总结出的一套工具集...

    lua源码导读---云风

    ### lua源码导读---云风 #### 概览 **Lua** 是一门轻量级、高效能的脚本语言,广泛应用于游戏开发、系统管理工具、网络应用等多个领域。本书《lua源码导读》旨在深入剖析 Lua 的源代码,帮助读者理解其内部实现...

    游戏之旅-我的编程感悟(云风编).pdf.part5.rar

    游戏之旅-我的编程感悟(云风编).pdf.part5.rar 本人发布,必是精品。 太简单的我不发。 太易找的我不发。 太经典的我定发。

    云风《Lua源码欣赏》1积分

    这些部分包括虚拟机运转的核心功能,如C语言接口、C标准库中ctype相关实现、Debug接口、函数调用及栈管理、函数原型及闭包管理、垃圾回收、内存管理接口、对象操作、虚拟机的字节码定义以及全局状态机等。...

    云风《风魂2D游戏引擎》

    云风的风魂引擎源代码。VS2010+WIN7编译通过

    《那些日子》- 云风

    云风开始编程是从 DOS 时代开始的,他研究过 allegro 这个游戏开发库,并翻译了 allegro 的文档。他在 allegro 的 maillist 中说过话,并优化了游戏引擎的代码。云风也提出了自己的想法,但是 allegro 的作者 Shawn ...

    云风WebShell管理器.rar

    1. **WebShell上传与下载**:允许用户将WebShell文件上传到目标服务器,同时也能下载已存在的WebShell,便于分析和研究。 2. **命令执行**:通过WebShell接口,用户可以远程执行服务器上的各种操作系统命令,如查看...

    云风pbc windows下xx.proto生成xx.pb 工具

    总结来说,"云风pbc windows下xx.proto生成xx.pb工具"简化了Windows环境下protobuf的编译过程,通过运行bat脚本即可快速生成.pb文件。这对于开发者来说,既节省了时间,又避免了手动操作的复杂性。在实际的软件开发...

    我的编程感悟(附:云风写的风魂++2D引擎源码)

    我的编程感悟(附:云风写的风魂++2D引擎源码),很实在,很有用的文章

Global site tag (gtag.js) - Google Analytics