在阅读“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变量,我认为它造成了混乱。所以我修改了代码。
编辑: 是的,正如乔恩所说 - 这是一个神话,我应该提到这一点。我很抱歉。
答案 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)紧张相关:
引用类型对象通常在GC堆上分配。但我知道一个特定的异常,源代码中从文字产生的实习字符串在AppDomain的加载器堆中分配。这完全像运行时的对象,除了它没有链接到GC堆,收集器根本看不到它。
解决您的代码段:
答案 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应用程序中的存储方式和位置:
变量的内存插槽存储在堆栈或 堆。它取决于声明它的上下文:
- 每个局部变量(即在方法中声明的变量)都存储在堆栈中。这包括引用类型变量 - 变量本身 在堆栈上,但请记住引用类型变量的值 只是一个引用(或null),而不是对象本身。方法 参数也计为局部变量,但如果它们是声明的 ref修饰符,他们没有自己的插槽,但共享一个插槽 调用代码中使用的变量。请参阅我的参数文章 传递了更多细节。
- 引用类型的实例变量始终在堆上。这就是物体本身“存在”的地方。
- 值类型的实例变量存储在与声明值类型的变量相同的上下文中。内存槽为 实例有效地包含了每个字段的插槽 实例。这意味着(给出前两点)结构 在方法中声明的变量将始终在堆栈上,而 一个struct变量,它是一个类的实例字段 堆。
- 每个静态变量都存储在堆上,无论它是在引用类型还是值类型中声明。只有 无论创建多少个实例,总共一个插槽。 (那里 不需要为该一个插槽创建任何实例 虽然。)变量所依赖的堆的详细信息是 复杂,但在MSDN文章中有详细解释 主题。
醇>