我正在通过Ferret(Lucene的Ruby端口)代码解决问题 一个bug。 Ferret代码主要是Ruby的C扩展。我遇到了 垃圾收集器的一些问题。我设法解决了它,但我 不完全理解我的修复=)我希望有更深层次的人 Ruby和C扩展的知识(这是我在Ruby的第3天)可以 阐述。感谢。
情况如下:
在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中,此代码与以下内容相关:
token = @ input.next
基本上,@ input设置为某个对象,在其上调用下一个方法 触发get_token C调用,返回一个令牌对象。
在Ruby领域,我做了类似w = token.text.scan('\ w +')
的事情。当我在while 1循环中运行此代码(以隔离我的问题)时,at 某点(大约当我的ruby进程mem足迹达到256MB时, 可能是一些GC阈值),Ruby死了像
这样的错误调用终止对象的扫描方法
或者只是核心转储。我的猜测是token.text被垃圾收集了。
我对Ruby C扩展知之甚少,不知道会发生什么 Data_Wrap_Struct返回了对象。在我看来Ruby中的任务 land,token =,应该创建对它的引用。
我的“解决方法”/“修复”是在中创建一个Ruby实例变量 @input引用的对象,并将令牌文本存储在其中 得到一个额外的参考。所以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的类的免费回调中。
使用此代码,它的作用是我不再获得终止的对象 错误。
修复似乎对我有意义 - 它保留了额外的参考 到token.text字符串,因此不会删除token.text的实例 直到下一次调用@ input.next(此时不同 token.text替换curtoken中的旧值。
我的问题是:为什么以前不起作用?不能 Data_Wrap_Structure返回一个对象,当在Ruby域中分配时, 有一个有效的引用,不被Ruby删除?
感谢。
答案 0 :(得分:3)
当调用Ruby垃圾收集器时,它具有标记阶段和扫描阶段。标记阶段通过标记来标记系统中的所有对象:
以及对此讨论不重要的许多其他对象。扫描阶段然后销毁任何不可访问的对象(即那些未标记的对象)。
Data_Wrap_Struct返回对象的引用。只要该引用可用于ruby代码(例如存储在局部变量中)或位于堆栈上(由本地C变量引用),就不应该扫描该对象。
从您发布的内容看起来,令牌 - >文字正在收集垃圾。但为什么要收集?它一定不能被标记。令牌对象本身是否被标记?如果是,则应标记令牌 - >文本。尝试在令牌的标记功能中设置断点或打印消息以查看。
如果令牌没有被标记,那么下一步就是找出原因。如果它被标记,那么下一步是弄清楚为什么text()方法返回的字符串被扫描(也许它不是被标记的同一个对象)。
另外,你确定它是令牌的文本成员导致异常吗?看着:
http://github.com/dbalmain/ferret/blob/master/ruby/ext/r_analysis.c
我看到令牌和令牌流都有text()方法。 TokenStream结构不包含对其文本对象的引用(它不能,因为它是一个不知道ruby的C结构)。因此,包装C结构的Ruby对象需要保存引用(这是通过rb_ivar_set完成的。)
RToken结构不需要这样做,因为它在其标记函数中标记其文本成员。
还有一件事:您可以通过在循环中显式调用GC.start来重现此错误,而不必分配垃圾收集器启动的那么多对象。这不会解决问题,但可能会进行诊断简单。
答案 1 :(得分:0)
或许标记为不稳定:
http://www.justskins.com/forums/chasing-a-garbage-collection-bug-98766.html
也许你的编译是在注册表而不是堆栈中保留它的引用...我在README.EXT中提到了一些方法来强制一个对象永远不会被GC,但是......问题仍然存在至于为什么要尽早收集...