繁体   English   中英

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

[英]Are these objects's references on the Stack or on the Heap?

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

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;
}

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

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语义的方式。

严格来说,它取决于实现。 通常,.NET开发人员不应该关心这些事情。 据我所知,在Microsoft .NET的实现中,值类型的变量存储在堆栈中(在方法中声明它们时),引用类型对象的数据分配在托管堆上。 但是请记住,当值类型是类的字段时,类数据本身会存储在堆中(包括所有值类型字段)。 因此,请勿将语义(值类型与引用类型)与分配规则混合使用。 这件事可能相关也可能不相关。

我想您可能会有一点误会...

一般而言,引用类型进入堆栈,而我认为(可能是错误的)值类型/本地变量进入堆栈。 但是,您的A1.VarA和A2.VarA示例引用的是引用类型的字段-该字段与对象一起存储在堆中...

在这种情况下,a1.VarA将在堆上,因为当您执行A a1 = new A()时将为其分配空间。

如果你只是做int i = 5; 在将要在堆栈上的函数中,但是正如您明确声明的那样,将a1分配在堆上,然后与之关联的所有值类型都将放置在堆上

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

通过C#阅读Jeff Richter的CLR ,可以全面了解该主题。

切记在C#中深入阅读:-只有局部变量(在方法内部声明的变量)和方法参数存在于堆栈中。实例变量如varA在上述情况下位于堆中。

我也是C#的新手。 您的问题非常重要,我也想到了。 所有文档都说,值进入堆栈,引用进入堆,但是正如上面的人所说,它仅用于方法内部的代码。 在学习的阶梯上,我意识到所有程序代码都始于一个方法的内部,该方法属于一个属于堆的实例。 因此,从概念上讲,堆栈在术语上与堆不相等,就像所有文档使人们感到困惑一样。 仅在一种方法中发现堆栈机制。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM