CLR如何处理对象字段的数组索引的访问

时间:2013-12-16 13:12:47

标签: c# .net mono clr

我正在寻找有关CLR访问数组索引/对象成员的C#代码中发生的事情的详细说明。请考虑以下事项:

int myVal = myObjArray[1].MyObjFieldB;

C#编译器将其编译为以下内容:

IL_0001:  ldc.i4.1
IL_0002:  ldelem.ref
IL_0003:  ldfld      int32 MyProgram.myObj::MyObjFieldB
IL_0008:  pop

但执行程序时会发生什么? CLR如何确定数组和对象的正确内存位置?它如何确保GC不会干扰?

基于Mono的答案非常有价值(尤其是指向源代码的指针)。

2 个答案:

答案 0 :(得分:1)

“干扰”并不是一个正确的词,它是GC找回指针并更新其值的重要职责之一。

首先找到指针是最重要的工作,这就是GC知道数组仍在使用而不应该被收集的方式。抖动在这方面起着非常重要的作用。当它将IL转换为机器代码时,它会执行生成代码的明显且非常明显的工作。但是还完成另一个完全不可见的工作,它会生成一个表,该表描述了对象引用存储在方法中的确切位置。它包含CPU寄存器和堆栈帧位置的表条目,由代码地址索引。 GC需要此表来找回对象引用。

因此,在构建对象图之后,确定哪些对象仍然存在并压缩堆,它最后做的是在移动对象时更新指针值。修补堆栈位置或存储的CPU寄存器值。因此,在代码恢复后,它现在再次使用正确的指针。

答案 1 :(得分:0)

第1步:

加载数组引用。这可以通过基本上任何加载操作来完成,例如ldloc.0(加载局部变量0)

筹码:

Reference to myObjArray

第2步:

加载索引。这也可以通过任何加载操作完成,例如ldc.i4.1(加载常量为4字节int,值为1)

筹码:

1
Reference to myObjArray

第3步:

索引到数组中。这是通过ldelem(Load element)操作码系列完成的。这里是ldelem.ref(加载元素引用),因为它正在从数组加载引用。

筹码:

Reference to myObjArray[1]

第4步:

使用ldfld(加载字段值)并传递字段来获取字段。

筹码:

myObjArray[1].MyObjFieldB

第5步:

存储值。这可能是通过stloc(存储到局部变量)操作码完成的,但在您的情况下,它只是被pop丢弃。


.NET中的所有引用都只是内存地址,存储方式与普通变量非常相似。

如果GC在此期间运行,它将不会删除任何内容,并且它将透明地重新分配所有对新值的引用,并且您的代码甚至不会注意到。