gpt4 book ai didi

ruby - 使用 Ruby C Extension 进行垃圾收集

转载 作者:数据小太阳 更新时间:2023-10-29 07:18:09 25 4
gpt4 key购买 nike

我正在通过 Ferret(Lucene 的 Ruby 端口)代码解决问题一个错误。 Ferret 代码主要是 Ruby 的 C 扩展。我遇到了垃圾收集器的一些问题。我设法修复了它,但我不完全理解我的修复=)我希望有人更深入了解 Ruby 和 C 扩展(这是我使用 Ruby 的第三天)可以精心制作的。谢谢。

情况是这样的:

在 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 调用,它返回一个 token 对象。

在 Ruby 领域,我会做类似 w = token.text.scan('\w+') 的事情

当我在 while 1 循环中运行此代码(以隔离我的问题)时,在某个点(大约当我的 ruby​​ 进程内存占用达到 256MB 时,可能是一些 GC 阈值),Ruby 死于类似这样的错误

在终止对象上调用的扫描方法

或者只是核心转储。我的猜测是 token.text 被垃圾收集了。

我对 Ruby C 扩展的了解还不够,不知道会发生什么Data_Wrap_Struct 返回对象。在我看来,Ruby 中的任务land, token =, 应该创建对它的引用。

我的“解决方法”/“修复”是在@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 类的自由回调中的 this 引用。

使用这段代码,它的工作原理是我不再得到终止的对象错误。

这个修复对我来说似乎很有意义——它在 curtoken 中保留了一个额外的引用到 token.text 字符串,因此不会删除 token.text 的实例直到下一次 @input.next 被调用(此时一个不同的token.text 替换了 curtoken 中的旧值。

我的问题是:为什么以前不行?不应该Data_Wrap_Structure 返回一个对象,当在 Ruby land 中分配时,有一个有效的引用并且没有被 Ruby 删除?

谢谢。

最佳答案

当调用 Ruby 垃圾收集器时,它有一个标记阶段和一个清除阶段。标记阶段通过标记标记系统中的所有对象:

  1. ruby 堆栈框架引用的所有对象(例如局部变量)
  2. 所有可全局访问的对象(例如,由常量或全局变量引用)及其子对象/引用对象,以及
  3. 堆栈上的引用引用的所有对象,以及这些对象的子对象/引用对象。

以及一些对本次讨论不重要的其他对象。然后,扫描阶段会销毁所有不可访问的对象(即未标记的对象)。

Data_Wrap_Struct 返回一个对象的引用。只要该引用可用于 ruby​​ 代码(例如,存储在局部变量中)或在堆栈上(由局部 C 变量引用),就不应清除该对象。

从您发布的内容看来,token->text 正在被垃圾收集。但为什么它会被收集起来?它不能被标记。 Token 对象本身是否被标记?如果是,那么 token->text 应该被标记。尝试在 token 的标记功能中设置断点或打印消息以查看。

如果 token 没有被标记,那么下一步就是找出原因。如果它被标记了,那么下一步就是弄清楚为什么 text() 方法返回的字符串被清除了(也许它不是被标记的同一个对象)。

此外,您确定是 token 的文本成员导致了异常吗?看着:

http://github.com/dbalmain/ferret/blob/master/ruby/ext/r_analysis.c

我看到 token 和 token 流都有 text() 方法。 TokenStream 结构不持有对其文本对象的引用(它不能,因为它是一个不了解 ruby​​ 的 C 结构)。因此,包装 C 结构的 Ruby 对象需要保存引用(这是通过 rb_ivar_set 完成的)。

RToken 结构不需要这样做,因为它在其标记函数中标记了它的文本成员。

还有一件事:您可以通过在循环中显式调用 GC.start 来重现此错误,而不必分配垃圾收集器启动的那么多对象。这不会解决问题,但可能会进行诊断更简单。

关于ruby - 使用 Ruby C Extension 进行垃圾收集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2003885/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com