This great answer解释了GC在方法执行完毕之前如何收集局部变量:
当将一个方法的IL编译成机器代码时,抖动执行两个重要的任务。 ... 它还会生成一个表,用于描述如何使用方法体内的局部变量。该表有每个方法参数的条目和具有两个地址的局部变量。变量首先存储对象引用的地址。以及不再使用该变量的机器代码指令的地址。 ... "不再使用"表中的地址非常重要。它使垃圾收集器非常有效。它可以收集一个对象引用,即使它在一个方法中使用,并且该方法还没有完成执行。
我很好奇JIT如何创建内部表格,以及如何"不再使用"地址保存在真实clr源代码中。任何人都可以在最近开源的coreclr source code中向我展示相关的代码片段吗?
答案 0 :(得分:4)
免责声明:我不是CLR或RyuJIT的专家。我可能完全错了这一切。
我在section的RyuJIT chapter中遇到了以下Book of the Runtime:
对于具有跟踪生命周期的lvlVars,或涉及GC引用的表达式,我们报告引用的实际范围。这是由发射器完成的,它将此信息添加到指令组,并在GC信息更改时终止指令组。
可以在jit/jitgcinfo.h中找到存储此信息的结构,如下所示:
struct varPtrDsc
{
varPtrDsc * vpdNext;
unsigned vpdVarNum; // which variable is this about?
unsigned vpdBegOfs ; // the offset where life starts
unsigned vpdEndOfs; // the offset where life starts
};
我上面引用的段落表明这些字段由"发射器"填充,我认为它们的意思是jit/emit.cpp。
生命周期的开始时间设置在emitter::emitGCvarLiveSet()
;相关的摘录是(为简洁而删除的空白):
/* Allocate a lifetime record */
desc = new (emitComp, CMK_GC) varPtrDsc;
desc->vpdBegOfs = emitCurCodeOffs(addr);
#ifdef DEBUG
desc->vpdEndOfs = 0xFACEDEAD;
#endif
desc->vpdVarNum = offs;
desc->vpdNext = NULL;
生命周期的结束以类似的方式设置,emitter::emitGCvarDeadSet()
:
/* Record the death code offset */
assert(desc->vpdEndOfs == 0xFACEDEAD);
desc->vpdEndOfs = emitCurCodeOffs(addr);
最后,表格似乎是用jit/gcencode.cpp写的,特别是GCInfo::gcMakeVarPtrTable()
。
如果您想进一步探索,希望这将成为一个起点。