使用Ruby C Extension进行垃圾收集

时间:2010-01-05 02:55:25

标签: ruby garbage-collection ferret

我正在通过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删除?

感谢。

2 个答案:

答案 0 :(得分:3)

当调用Ruby垃圾收集器时,它具有标记阶段和扫描阶段。标记阶段通过标记来标记系统中的所有对象:

  1. ruby​​堆栈框架引用的所有对象(例如局部变量)
  2. 所有全局可访问的对象(例如,由常量或全局变量引用)及其子/对象,以及
  3. 堆栈上引用引用的所有对象,以及这些对象的子对象/对象。
  4. 以及对此讨论不重要的许多其他对象。扫描阶段然后销毁任何不可访问的对象(即那些未标记的对象)。

    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,但是......问题仍然存在至于为什么要尽早收集...