我有一个嵌入了 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 是否包含这样的东西,我找不到类似的东西。