我正在通过 Ferret(Lucene 的 Ruby 端口)代码解决错误。Ferret 代码主要是 Ruby 的 C 扩展。我遇到了垃圾收集器的一些问题。我设法修复它,但我不完全理解我的修复 =) 我希望对 Ruby 和 C 扩展有更深入了解的人(这是我使用 Ruby 的第三天)可以详细说明。谢谢。
情况如下:
在 Ferret C 代码中的某些地方,我将“令牌”返回给 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) 释放令牌
在 Ruby 中,此代码与以下内容相关:
令牌 = @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的类的免费回调中删除/删除这个引用。
使用此代码,它的工作原理是我不再收到终止的对象错误。
这个修复对我来说似乎很有意义——它在 curtoken 中为 token.text 字符串保留了一个额外的 ref,因此在下次调用 @input.next 之前不会删除 token.text 的实例(此时 a不同的 token.text 替换 curtoken 中的旧值)。
我的问题是:为什么它以前不起作用?Data_Wrap_Structure 不应该返回一个对象,当在 Ruby 领域分配时,该对象具有有效的引用并且不会被 Ruby 删除?
谢谢。