我正在寻找有关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
的答案非常有价值(尤其是指向源代码的指针)。
答案 0 :(得分:1)
“干扰”并不是一个正确的词,它是GC找回指针并更新其值的重要职责之一。
首先找到指针是最重要的工作,这就是GC知道数组仍在使用而不应该被收集的方式。抖动在这方面起着非常重要的作用。当它将IL转换为机器代码时,它会执行生成代码的明显且非常明显的工作。但是还完成另一个完全不可见的工作,它会生成一个表,该表描述了对象引用存储在方法中的确切位置。它包含CPU寄存器和堆栈帧位置的表条目,由代码地址索引。 GC需要此表来找回对象引用。
因此,在构建对象图之后,确定哪些对象仍然存在并压缩堆,它最后做的是在移动对象时更新指针值。修补堆栈位置或存储的CPU寄存器值。因此,在代码恢复后,它现在再次使用正确的指针。
答案 1 :(得分:0)
加载数组引用。这可以通过基本上任何加载操作来完成,例如ldloc.0
(加载局部变量0)
筹码:
Reference to myObjArray
加载索引。这也可以通过任何加载操作完成,例如ldc.i4.1
(加载常量为4字节int,值为1)
筹码:
1
Reference to myObjArray
索引到数组中。这是通过ldelem
(Load element)操作码系列完成的。这里是ldelem.ref
(加载元素引用),因为它正在从数组加载引用。
筹码:
Reference to myObjArray[1]
使用ldfld
(加载字段值)并传递字段来获取字段。
筹码:
myObjArray[1].MyObjFieldB
存储值。这可能是通过stloc
(存储到局部变量)操作码完成的,但在您的情况下,它只是被pop
丢弃。
.NET中的所有引用都只是内存地址,存储方式与普通变量非常相似。
如果GC在此期间运行,它将不会删除任何内容,并且它将透明地重新分配所有对新值的引用,并且您的代码甚至不会注意到。