1

我有一个嵌入了 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 结构在内存中占用的实际大小,这将是一个不错的解决方案,但我不知道 ruby​​ api 是否包含这样的东西,我找不到类似的东西。

4

1 回答 1

0

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

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

于 2012-09-17T06:46:44.697 回答