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 を定期的に呼び出すことでこの問題を解決できますが、私にとっては汚い解決策のように思えます。
一部のオブジェクトが孤立したときにガベージ コレクションを優先するようにする方法や、c 構造体がメモリ内で占有する実際のサイズを Ruby に伝える方法があれば、良い解決策ですが、Ruby API にこのようなものが含まれているかどうかはわかりません。私はそのようなものを見つけることができませんでした。