引用类型存在于堆上,值类型存在于堆栈中

时间:2010-08-22 15:44:38

标签: c#

在阅读“C#in Depth”时,我正在阅读“引用类型存在于堆上,值类型存在于堆栈中。”

现在我能理解的是(主要用于ref类型):

class Program
{
    int a = 5;  // stored in heap

    public void Add(int x, int y) // x,y stored in stack
    {
        int c = x + y;  // c  stored in stack
    }
}

只想澄清我的假设是否正确。谢谢。 编辑: 我应该使用diff变量,我认为它造成了混乱。所以我修改了代码。

编辑: 是的,正如乔恩所说 - 这是一个神话,我应该提到这一点。我很抱歉。

6 个答案:

答案 0 :(得分:14)

http://blogs.msdn.com/b/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx

整个“堆上的引用类型,堆栈上的值类型”不仅是一种看待它的坏方法,而且也是错误的。

答案 1 :(得分:10)

对于幕后发生的事情,我可能是一个有用的抽象概念。但在任何当前发布的JIT编译器版本中都不是这样。这可能是问题的关键,实际的分配位置是JIT编译器实现细节。

至少有六个地方的值类型值可以与主流(x86和x64)紧张相关:

  • 在堆栈框架中,通过局部变量声明或方法调用放在那里
  • 在CPU寄存器中,由JIT在Release版本中执行的非常常见的优化。并且用于将参数传递给方法,前两个x86,四个用于x64。和可能的局部变量
  • FPU堆栈上的
  • ,由x86抖动用于浮点值
  • 在GC堆上,当值是引用类型
  • 的一部分时
  • 在AppDomain的加载器堆上,当变量声明为static
  • 时 当变量具有[ThreadStatic]属性时,
  • 在线程局部存储中。

引用类型对象通常在GC堆上分配。但我知道一个特定的异常,源代码中从文字产生的实习字符串在AppDomain的加载器堆中分配。这完全像运行时的​​对象,除了它没有链接到GC堆,收集器根本看不到它。

解决您的代码段:

  • 是的,“a”很可能存储在GG堆
  • “x”始终在x86和x64上的CPU寄存器中传递。 “y”将位于x64上的CPU寄存器中,即x86上的堆栈。
  • “c”可能根本不存在,由JIT编译器删除,因为代码没有效果。

答案 2 :(得分:1)

c离开堆栈,因为至少是托管堆中a的值类型,因为它是引用类型的字段

答案 3 :(得分:0)

引用类型的存储位置(变量,字段,数组元素等)包含对堆上对象的引用;原始价值类型的存储位置在其自身内部保持其价值; struct类型的存储位置包含它们的所有字段,每个字段本身可以是引用或值类型。如果一个类实例包含两个不同的非空字符串,一个Point和一个整数,那么该点的X和Y坐标,以及独立的整数和对这两个字符串的引用都将保存在一个堆中宾语。每个字符串将保存在不同的堆对象中。关于类与结构的存储位置的关键点是,除了类实体持有对自身的引用之外,类或结构中的每个非空引用类型字段都将保存对某些 other <的引用。 / i>对象,它将在堆上。

答案 4 :(得分:0)

用C / C ++术语来思考它。

任何时候你做一个“新”的东西,或者使用malloc,它就在堆上 - 也就是说,“对象”在堆上,指针本身被放置在结构范围内的堆栈上(或功能,它实际上只是另一种结构)它是它的一部分。如果它是局部变量或引用类型(指针),它将进入堆栈。

换句话说,&gt;对象&lt;引用类型指向的是在堆上,它只是堆栈上的指针本身。当程序将指针弹出堆栈时会发生内存泄漏,但堆中的内存尚未被释放以供使用 - 如果对其位置的引用已丢失,您如何知道释放它的内存?好吧,C / C ++不可能,你必须在引用从堆栈中弹出并永远丢失之前自己动手,但这就是现代语言带来他们花哨的shmancy“垃圾收集堆”的地方。通过将GC保留为拾取来显式清除您分配的任何堆内存仍然是可取的,这种方式“更便宜”(就CPU资源而言)。

答案 5 :(得分:0)

从他的famous blog中引用Jon Skeet,了解参考和价值类型在.Net应用程序中的存储方式和位置:

  

变量的内存插槽存储在堆栈或   堆。它取决于声明它的上下文:

     
      
  1. 每个局部变量(即在方法中声明的变量)都存储在堆栈中。这包括引用类型变量 - 变量本身   在堆栈上,但请记住引用类型变量的值   只是一个引用(或null),而不是对象本身。方法   参数也计为局部变量,但如果它们是声明的   ref修饰符,他们没有自己的插槽,但共享一个插槽   调用代码中使用的变量。请参阅我的参数文章   传递了更多细节。
  2.   
  3. 引用类型的实例变量始终在堆上。这就是物体本身“存在”的地方。
  4.   
  5. 值类型的实例变量存储在与声明值类型的变量相同的上下文中。内存槽为   实例有效地包含了每个字段的插槽   实例。这意味着(给出前两点)结构   在方法中声明的变量将始终在堆栈上,而   一个struct变量,它是一个类的实例字段   堆。
  6.   
  7. 每个静态变量都存储在堆上,无论它是在引用类型还是值类型中声明。只有   无论创建多少个实例,总共一个插槽​​。 (那里   不需要为该一个插槽创建任何实例   虽然。)变量所依赖的堆的详细信息是   复杂,但在MSDN文章中有详细解释   主题。
  8.