如何在 Ruby 扩展中的 VALUE* 数组上完成标记?
-
12-12-2019 - |
题
我有一个矩阵类型,其中包含 void*
数组,表示对象数组(给定矩阵中都是一种类型,例如,所有 C 整数、所有浮点数、双精度数、各种结构,甚至可能是所有 Ruby VALUE
s)。
内存分配和垃圾收集似乎工作正常,直到我尝试创建一个矩阵 VALUE
s。
我定义了以下标记函数:
void mark_dense_storage(void* s) {
size_t i;
DENSE_STORAGE* storage = (DENSE_STORAGE*)s;
if (storage && storage->dtype == RUBY_OBJECT)
for (i = 0; i < count_dense_storage_elements(s); ++i)
rb_gc_mark(*((VALUE*)(storage->elements + i*sizeof(VALUE)));
}
所以它只在它实际上是一个时才做标记 VALUE
矩阵——否则, NULL
被传递给 Data_Wrap_Struct
为标记功能。
但是当我测试一些时出现段错误 VALUE
矩阵函数 (见要点).
具体来说,当我第一次尝试在第一个对象上调用 Ruby 方法时,似乎出现了段错误 VALUE*
大批:
C[i+j*ldc] = rb_funcall(C[i+j*ldc], nm_id_mult, 1, beta); // C[i+j*ldc] = C[i+j*ldc]*beta
nm_id_mult
是我的全局定义 Init
充当 rb_intern("*")
.
这可能不是垃圾收集问题,但 GC 是 Ruby 中我了解最少的部分——而且我的段错误也几乎与 这条痕迹, ,海报将其归因于 GC。
所以,我的问题是:
如果是 GC,标记数组的适当方法是什么
VALUE
是?如果不是 GC,我该如何诊断此类错误?我从来没有见过这样的事情。
编辑:
原来这是一个初始化失败的例子 VALUE
s 在 C 中创建。
换句话说,请确保执行以下操作 *(VALUE*)a = INT2FIX(0)
在您尝试访问之前 a
.
我仍然认为这个问题是相关的。我还没有在 StackOverflow 或其他地方找到任何真正好的标记干净和清除垃圾收集的示例。如果您可以提供这样的示例和/或解释,我会将其标记为该问题的正确答案。
解决方案
Ruby 的标记和清除 GC 分两个阶段工作。
第一阶段标记活动对象。它通过调用每个已知“活动对象”的标记函数来递归地工作。初始的活动对象集是通过扫描每个已知 Ruby 线程或每个已注册全局对象的 C 堆栈来生成的(有一个 C 函数用于注册/取消注册“已知活动”对象)。然后,对象 X 的标记函数应该为 X 引用的每个对象调用 rb_gc_mark。换句话说,你所做的正是你应该做的。
然而,正如您稍后注意到的,并非任何可能的 VALUE 都是有效的 Ruby 对象。然而,我相信用 Qnil 初始化(即nil) 会更加红宝石风格。