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

在阅读“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 } } 

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

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

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

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

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

至少有六个地方的值类型值可以与主流(x86和x64)抖动一起使用:

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

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

解决您的代码段:

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

c离开堆栈,因为至少是一个值类型,同时在托管堆中是一个值,因为它是引用类型的字段

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

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

无论什么时候你做一个“新”的东西,或者使用malloc,它都在堆上 – 也就是说,“对象”在堆上,指针本身被放置在结构范围内的堆栈(或函数,它只是另一种结构而已成为其中的一部分。 如果它是局部变量或引用类型(指针),它将进入堆栈。

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

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

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

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