您的位置:首页 > 编程语言 > Ruby

Ruby的GC机制源码分析(3)

2010-12-18 13:54 134 查看

对象的管理

ruby

GC
的目标只是Ruby
的对象。而且一定要是
ruby

生成和管理的对象。反过来说,它无法照料到用户随意分配的内存。比如,下面的函数在
ruby

的操作中就会引起内存泄漏

void not_ok(){
malloc(1024);
/*
获得内存后丢弃 */

}

然而下面的函数不会引起内存泄漏。

void this_is_ok(){
rb_ary_new();

/*
生成Ruby
数组后丢弃 */

}

[code]rb_ary_new()


使用了
ruby

的正式接口分配内存,所以会在
ruby

GC
的管理之下,由
ruby

照料。
[/code]

struct RVALUE


对象的实体是结构体,对象的管理就是对这个结构体的管理。当然,非指针的
Fixnum Symbol nil true false

例外,太麻烦,这里就不一一写了。

实体结构体的大小因类型而不同,恐怕是为了避免管理上的麻烦,内建类的结构体声明为共用体,并通过共用体访问内存。共用体声明如下。



RVALUE



struct
RVALUE

是一个只有一个元素的结构体。不直接使用
union

是为了调试或将来扩展时添加成员的方便。

首先来关注一下共用体的第一个元素
free.flags

。注释中写着“
不用时为0”
,这是真的吗?难道使用中的对象
free.flags

不能偶然为0
吗?

正如在第2
章《对象》中看到的那样,所有的对象构造体其第一个元素都是
struct RBasic

。因此,无论从共用体的哪个元素访问,写成
obj->as.free.flags

和写成
obj->as.basic.flags

都是一样的。对象在标志位都有一个结构体类型标志(
T_STRING

等),而且,所有的标志都是非0
值,所以, “
活着”
的对象其标志不会偶然为0
。换句话说,可以确认,标志为0
是“
死”
对象的充分必要条件。

对象堆(
object heap



所有对象结构的内存都在全局的变量堆上。下面这个叫做对象堆。



对象堆


heaps

struct RVALUE

数组的数组。
heaps

中保存的是一个个的
heap


heap

的元素就是一个个的
slot

(图9
)。



图9:
heaps


heap

slot


heaps

的长度
heaps_length

是可变的。实际用到的槽的个数保存
heaps_used

中。每个
heap

的长度对应保存在
heaps_limits[index]

中。也就是说,对象堆的结构如图10
所示。



图10:
在内存上展开的
heap

的概念图

这个结构有其必然性。比如,当所有结构都配置到一个数组中时,内存空间最为紧凑,但是由于地址可能发生变化,不能使用
realloc()

,因为
VALUE

就是单纯的指针。

对应于Java
实现,对象是可以移动的,因为它是通过对象表来处理的,
VALUE

是对象的索引,而非地址。然而,每次访问对象多要对数组进行索引,性能会有所下降。

另一方面,把
RVALUE

的指针(也就是
VALUE

)做成一个一维数组会怎么样呢?乍看起来,一切顺利,但GC
的时候会遇到问题。正如后面会详细讨论的,因为
ruby

的GC
需要知道一个整数是否是“
VALUE

(指向
RVALUE

的指针)一样”
。所有的
RVALUE

配置到不相关的地址之后,所有
RVALUE

的地址要分配同所有“
可能是地址”
的整数进行比较。它会让GC
挂起的时间变成O(n^2)
以上的数量级,这是无法容忍的。

综上所述,对象堆是在某种程度上将地址相关,且位置和总量不受限制的结构。

freelist

未使用
RVALUE

是由一个以
freelist

开头的链表管理的。
RVALUE

as.free.next

就是为此准备的链。



freelist


236
static RVALUE *freelist = 0

(gc.c)


add_heap()

了解数据结构之前,先来看看添加堆的函数
add_heap()

。这个函数主线之外的描述很杂乱,除去错误处理和转型部分,可以得到一个简化版本。



add_heap()

(简化版)

static void add_heap()

{

RVALUE *p, *pend;

/*
必要的话扩展heaps */

if (heaps_used == heaps_length) {

heaps_length += HEAPS_INCREMENT;

heaps= realloc(heaps,

heaps_length * sizeof(RVALUE*));

heaps_limits = realloc(heaps_limits, heaps_length * sizeof(int));

}


/*
增加一个heap */

p = heaps[heaps_used] = malloc(sizeof(RVALUE) * heap_slots);

heaps_limits[heaps_used] = heap_slots;

pend = p + heap_slots;

if (lomem == 0 || lomem > p) lomem = p;

if (himem < pend) himem = pend;

heaps_used++;

heap_slots *= 1.8;

/*
分配的RVALUE连接到freelist */

while (p < pend) {

p->as.free.flags = 0;

p->as.free.next = freelist;

freelist = p;

p++;

}

}

以下几点需要确认。


heap

的长度是
heap_slots


每增加一个
heap


heap_slots

变为原来的
1.8


heaps[i]

的长度(生成堆时
heap_slots

的值)保存在
heaps_limits[i]


再有,只有这个函数修改
lomem

himem

,也只有从这个函数理解其机制。这两个变量分别是对象堆的最下端地址和最上端地址。这个值稍后还会用于判断一个整数是否是“
VALUE

一样”
的。

rb_newobj()

综合以上几点,就能够知道对象生成的方法。如果
freelist

没有相连的
RVALUE

,就会去做GC
,或是增加堆。通过阅读对象生成的函数
rb_newobj()

,我们可以确认这一点。



rb_newobj()



如果
freelist

为0
,也就是,没有剩余的结构体,就启动GC
,创建一个区域。即便一个对象都无法收回,
rb_gc()

还可以分配一个新的区域,这是毫无疑问的。并且,从
freelist

中取出一个结构体,通过
MEMZERO()

用0
填充它,然后返回它。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: