以下是情况:
在Ferret C代码的某个地方,我将“Token”返回给Ruby。代码看起来像:
static VALUE get_token (...)
{
...
RToken *token = ALLOC(RToken);
token->text = rb_str_new2("some text");
return Data_Wrap_Struct(..., &frt_token_mark, &frt_token_free, token);
}
frt_token_mark调用rb_gc_mark(token->text),而frt_token_free只是使用free(token)释放了token。
在Ruby中,这段代码表示以下:
token = @input.next
基本上,@input被设置为某个对象,调用它的next方法会触发get_token C调用,返回一个令牌对象。
在Ruby领域中,我接下来做的事情类似于w = token.text.scan('\w+')
当我在while 1循环中运行此代码(为了分离我的问题)时,在某个时刻(大致上当我的Ruby进程内存占用量达到256MB时,可能是一些GC阈值),Ruby会因为出现错误而死亡,例如:
在终止的对象上调用扫描方法
或者只是核心转储。我猜想token.text已经被垃圾回收了。
我对Ruby C扩展不太了解,不知道返回的Data_Wrap_Struct对象会发生什么。在Ruby领域中,似乎赋值token =应该创建对它的引用。
我的“解决方法”/“修复方法”是在由@input引用的对象中创建一个Ruby实例变量,并将令牌文本存储在其中,以获得额外的引用。因此,C代码如下:
RToken *token = ALLOC(RToken);
token->text = rb_str_new2(tk->text);
/* added code: prevent garbage collection */
rb_ivar_set(input, id_curtoken, token->text);
return Data_Wrap_Struct(cToken, &frt_token_mark, &frt_token_free, token);
现在我已经在输入实例变量中创建了一个“curtoken”,并保存了文本的副本... 我已经小心地在@input类的free回调函数中删除/删除了这个引用。
使用此代码,它可以正常工作,因为我不再收到终止对象错误。
修复似乎对我有意义-它在curtoken中保留了对token.text字符串的额外引用,因此直到下一次调用@input.next(此时不同的token.text替换curtoken中的旧值)之前,token.text的实例才会被删除。
我的问题是:为什么以前不起作用? Data_Wrap_Structure不应该返回一个对象,在Ruby land中分配时具有有效引用并且不会被Ruby删除吗?
谢谢。