-
2006-11-02
管窥Ruby——Allocator
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://dreamhead.blogbus.com/logs/3751564.html
Allocator,熟悉编程的人一看到这个名字,自然就会把它与内存分配联系起来,再近一步,它通常是与对象初始化联系到一起。在《对象的生命》中,我讨论过对象初始化的过程,将它分为“分配内存”和“初始化对象”两个动作,Allocator主要就是承担分配内存的工作。
如果以Java之类可以合分配与初始化于一体的语言来实现Ruby,那是不是意味着可以将Allocator并入到初始化过程中去,从而将它从Ruby中消灭呢?我一开始确实这样想,但无情的现实总会恰如其分的站出来教育人。
与Ruby源码打交道,有一点需要谨记,我们面对的是两个层次的东西,一个是Ruby层次,一个是C层次。比如,在之前探讨过的关于Class的内容,属于定义在C层次上,而在Ruby代码中写的类都是定义在Ruby层次上。初读代码时,因为把二者混淆,所以,很长一段时间内,我都被它们之间恼人的关系说困扰。实际上,它们之间有如角色和演员,我们在“台上”看到的对象——Ruby对象,后面有一个真正的“表演者”——C对象。
如何将C层次的对象和Ruby层次的对象结合起来呢?
我们来看一下如何创建一个对象。我们知道,Ruby中也有“一切皆对象”的口号,类也不例外,所以在Ruby代码中创建对象,不像很多语言中那样有一个专门的new操作符,而是调用了类对象的一个new方法:
MyClass.new这个new方法在源码中是这样定义的:
rb_define_method(rb_cClass, "new", -1);
(object.c)主要的内容实现在rb_class_new_instance:
VALUE
rb_class_new_instance(argc, argv, klass)
int argc;
VALUE *argv;
VALUE klass;
{
VALUE obj;obj = rb_obj_alloc(klass);
rb_obj_call_init(obj, argc, argv);return obj;
}
(object.c)在这个对象的方法中,我们可以清楚看见,“分配内存”(rb_obj_alloc)和“初始化对象”(rb_obj_call_init)两个过程。可想而知,rb_obj_call_init完成的是调用Ruby中的对象初始化方法——“initialize”,这里不再赘述。我们关注一下rb_obj_alloc。
这个方法实际上就是调用前面所提到的Allocator,由它完成对象的内存分配。如果不定义自己的Allocator,那么按照对象继承的关系,我们用到就是超类的方法,在Ruby中,最底层的Allocator定义在Object类中:
rb_define_alloc_func(rb_cObject, rb_class_allocate_instance);
(object.c)其中rb_class_allocate_instance的实现如下:
static VALUE
rb_class_allocate_instance(klass)
VALUE klass;
{
NEWOBJ(obj, struct RObject);
OBJSETUP(obj, klass, T_OBJECT);
return (VALUE)obj;
}
(object.c)NEWOBJ是一个内存分配的过程,OBJSETUP设置了这个对象的几个字段。
一般在Ruby层次上定义的类都不会有自己的Allocator,这段代码告诉我们,对于这样类的对象,在底层对应的就是一个RObject的结构,不同类对象之间的差异仅仅是klass字段。这也是我在《管窥Ruby——类的变量》中解释rb_ivar_set中真正的主干是T_OBJECT的原因。
其实,至此所有的故事都可以结束。但是,Ruby中还有许多定义在C层次上的类,找个堂皇点的理由,使用C层次上的类可以比这种用法效率上稍微有些优势,因为RObject的所有实例变量都放到st_table中,显然这种方式没有直接访问来得快:
struct RObject {
struct RBasic basic;
struct st_table *iv_tbl;
};
(ruby.h)既然有定义C层次类的需求,那怎么才能把它加入到Ruby的体系之中呢?回头看看rb_class_new_instance,实际上,rb_obj_alloc和rb_obj_call_init这两个函数除了承担前面所说的工作之外,还有其它的角色,按照前面的讨论,要说的已经很清楚了,rb_obj_alloc用来初始化C层次的类,rb_obj_call_init用来初始化Ruby层次的类。
如果需要加入C层次的类,我们要做的就是加入一个Allocator,Ruby提供了这样一个函数:
void rb_define_alloc_func( VALUE classmod, VALUE(*func)() );这里classmod表示一个Ruby层次上的类,而Allocator中定义一个C层次上的类,通过这段函数,我们就将它们关联了起来:
下面是一个例子,来自Array的实现:
rb_define_alloc_func(rb_cArray, ary_alloc);
(array.c)ary_alloc的实现如下:
static VALUE
ary_alloc(klass)
VALUE klass;
{
NEWOBJ(ary, struct RArray);
OBJSETUP(ary, klass, T_ARRAY);ary->len = 0;
ary->ptr = 0;
ary->aux.capa = 0;return (VALUE)ary;
}
(array.c)回到最初的问题上,想让Allocator退出历史舞台还真不是一件容易的事情,因为它在分配内存之外,还扮演了初始化底层类的角色。如果采用相同的结构,即便用Java这样的语言可以避开内存分配,但底层类初始化的过程是无法避开的。当然,在这种情况下,还叫Allocator,名称上会有些歧义。
引用地址:










评论