嵌入式Ruby GC和包装结构

时间:2012-09-17 01:09:07

标签: c++ ruby garbage-collection

我有一个嵌入了ruby 1.9.3的win32控制台应用程序,我遇到了ruby GC和带有包含大数据指针的包装C结构的对象的问题。

经过一些测试,当孤立的对象占用一些内存时,ruby似乎会运行GC。问题是ruby没有考虑结构指针占用的内存大小,所以它不会启动GC,因为它认为那些孤立的对象太小而且不占用太多内存。

我制作了一个示例应用程序会崩溃,因为它在包装结构中创建了大量包含大数据的对象,这里是代码:

#include <ruby.h>

typedef struct TestClassStructS {
    byte* bytes;
} TestClassStruct;

static void testClassFree(TestClassStruct *p) {
    delete p->bytes;
    delete p;
}

VALUE testClassNew(VALUE klass) {
    TestClassStruct* ptr = new TestClassStruct();
    ptr->bytes = new byte[1024 * 1024 * 5]();
    VALUE obj = Data_Wrap_Struct(klass, NULL, testClassFree, ptr);
    rb_obj_call_init(obj, 0, 0);
    return obj;
}

VALUE testClassInitialize(VALUE self) {
    return self;
}

typedef VALUE (*rubyfunc)(...);

VALUE require_wrap(VALUE arg)
{
    return rb_eval_string("GC.enable; loop do; TestClass.new; end");
}

int main(int argc, char** argv[])
{
    RUBY_INIT_STACK;
    ruby_init();
    //freopen("CON", "w", stdout);
    ruby_init_loadpath();
    ruby_sysinit(&argc, argv);

    VALUE testClass = rb_define_class("TestClass", rb_cObject);
    rb_define_singleton_method(testClass, "new", (rubyfunc)testClassNew, 0);
    rb_define_method(testClass, "initialize", (rubyfunc)testClassInitialize, 0);

    int error;
    VALUE result = rb_protect(require_wrap, 0, &error);
    if (error) 
    {
        VALUE lasterr = rb_gv_get("$!");
        VALUE message = rb_obj_as_string(lasterr);

        printf(StringValuePtr(message));
    }

    return ruby_cleanup(0);
}

这不是一个真实的情况,但让我担心在某些情况下,如果GC未启动,我的应用程序可能会占用太多内存。

我可以解决这个问题,定期调用GC.start,但对我来说这似乎是一个肮脏的解决方案。

如果有一种方法可以让ruby在某些对象被孤立时优先处理垃圾收集,或者告诉ruby c struct在内存中占用的实际大小,这将是一个很好的解决方案,但我不知道ruby api是否包含某些内容像这样,我找不到那样的东西。

1 个答案:

答案 0 :(得分:0)

如果可以,请使用xmalloc(我认为来自ruby.h)来分配内存。也就是说,到目前为止,唯一的方法是确保分配的内存占下一个GC触发器。

在包装的C结构中注册了一个新的dsize函数,但似乎它(但是?)在ruby 1.9.3中没有使用