一尘不染

这些对象的引用是在堆栈上还是在堆上?

c#

如果有人能告诉我我是否了解我,我将不胜感激:

class X
{
   A a1=new A(); // reference on the stack, object value on the heap
   a1.VarA=5;    // on the stack - value type
   A a2=a1;      // reference on the stack, object value on the heap
   a2.VarA=10;   // on the stack - value type         
}

而且a1a2引用都在堆栈上,而它们的“对象”值在堆上。但是VarA变量,它仍然是纯值类型呢?

class A
{
   int VarA;
}

阅读 816

收藏
2020-05-19

共1个答案

一尘不染

您在询问有关 实现细节的 问题,因此答案将取决于特定的实现。让我们考虑一下实际编译的程序版本:

class A { public int VarA; }
class X
{
    static void Main(string[] args)
    {
        A a1 = new A();
        a1.VarA = 5;
        A a2 = a1;
        a2.VarA = 10;
    }
}

这是在调试模式下运行C#4.0的Microsoft CLR 4.0上发生的情况。

此时,堆栈帧指针已复制到寄存器ebp中:

在这里,我们为新对象分配堆内存。

A a1 = new A();
mov         ecx,382518h 
call        FFE6FD30

这将返回对eax中堆对象的引用。我们将引用存储在堆栈插槽ebp-48中,这是一个与任何名称都没有关联的临时插槽。请记住,a1尚未初始化。

mov         dword ptr [ebp-48h],eax

现在,我们使用刚刚存储在堆栈中的引用,并将其复制到ecx中,它将用作指向ctor调用的“ this”指针。

mov         ecx,dword ptr [ebp-48h]

现在我们称为ctor。

call        FFE8A518

现在,我们将存储在临时堆栈插槽中的引用再次复制到寄存器eax中。

mov         eax,dword ptr [ebp-48h]

现在,我们将eax中的引用复制到堆栈插槽ebp-40(即a1)中。

mov         dword ptr [ebp-40h],eax

现在我们必须将a1提取到eax中:

a1.VarA = 5;
mov         eax,dword ptr [ebp-40h]

记住,eax现在是a1所引用的事物的堆分配数据的地址。那东西的VarA字段是对象中的四个字节,因此我们在其中存储5个字节:

mov         dword ptr [eax+4],5

现在,我们将a1的堆栈插槽中的引用复制到eax,然后将其复制到a2的堆栈插槽中,即ebp-44。

A a2 = a1;
mov         eax,dword ptr [ebp-40h] 
mov         dword ptr [ebp-44h],eax

现在,正如您再次期望的那样,我们将a2放入eax中,然后将参考引用四个字节,以将0x0A写入VarA:

a2.VarA = 10;
mov         eax,dword ptr [ebp-44h] 
mov         dword ptr [eax+4],0Ah

因此,对于您的问题的答案是,对对象的引用存储在堆栈中的三个位置:ebp-44,ebp-48和ebp-40。它们存储在eax和ecx中的寄存器中。对象的内存(包括其字段)存储在托管堆中。这一切都在Microsoft
CLR
v4.0的调试版本的x86上完成。如果您想知道东西如何在其他配置中存储在堆栈,堆和寄存器中,则可能完全不同。引用可以全部存储在堆中,也可以全部存储在寄存器中。可能根本没有堆栈。这完全取决于jit编译器的作者决定实现IL语义的方式。

2020-05-19