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

本文关键字:字段 对象 数组 访问 索引 何处理 处理 CLR | 更新日期: 2024-10-21 11:53:19

我正在寻找访问数组索引/对象成员的C#代码的CLR中发生的事情的详细描述。考虑以下内容:

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的答案同样有价值(尤其是对于指向源的指针)。

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

"干扰"这个词不完全正确,GC的重要职责之一是找到指针并更新其值。

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

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

步骤1:

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

堆栈:

Reference to myObjArray

第2步:

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

堆栈:

1
Reference to myObjArray

步骤3:

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

堆栈:

Reference to myObjArray[1]

第4步:

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

堆栈:

myObjArray[1].MyObjFieldB

步骤5:

存储值。这可能会用stloc(存储到本地变量)操作码来完成,但在您的情况下,它只是用pop来丢弃。


中的所有引用。NET是简单的内存地址,存储起来很像普通变量。

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