类中声明的变量的内存分配

Posted

技术标签:

【中文标题】类中声明的变量的内存分配【英文标题】:Memory Allocation for Variable Declared in Class 【发布时间】:2011-11-12 16:49:44 【问题描述】:

作为值类型变量在堆栈中分配内存,而作为引用类型在堆中分配内存。

那么当值类型变量(例如 int i =4;)在引用类型中(例如在类中)声明时,内存是如何分配的。

对于值类型和引用类型,以及引用类型范围内的值类型,.net 中的整体内存分配如何工作。

请解释或提供任何相关链接。

谢谢

【问题讨论】:

【参考方案1】:

值类型变量在栈上分配内存,而引用类型在堆上分配内存。

不,这种说法是完全错误的。很多人都相信这一点,但正如您所发现的,这显然是错误的。

当值类型变量int i = 4;被声明为引用类型的字段时,内存是如何分配的?

很明显,您知道为什么您的第一个陈述是完全错误的。类的整数字段不能在堆栈上分配,因为对象的寿命可能比堆栈帧长。

要了解真正发生的事情,首先你必须意识到有三种种事情:

值类型 参考文献 引用类型的实例

引用引用类型的实例完全不同,就像一张包含我的地址和我的实际房子的纸完全不同。

接下来你要了解的是,存储有两种:长期存储和临时存储。长期存储通常称为“堆”,但我更愿意将其简单地视为长期存储。临时存储通常称为“堆栈”,但这也具有误导性,因为当然可能有多个堆栈,也可能有临时存储在寄存器中,等等。

引用类型的实例在长期存储中占用内存。 (有时可以确定引用类型的实例是短暂的,并将其放入临时存储中,但实际上我们不会进行此优化。)

变量是一个存储位置,用于存储值类型的值引用

变量的存储位置分配在哪里取决于变量的生命周期。如果变量是已知生命周期短的局部变量,则从临时存储池中分配。如果已知变量的生命周期很长(例如,因为它是闭包的外部变量),那么它将被分配到长期存储池之外。

如果变量是一个类的字段,我们已经知道它的存储来自长期池。如果变量是值类型的字段,则该值类型存储在某处;该字段位于相同的存储空间中。

如果变量是数组元素,则从长期存储池中分配出去;数组是引用类型的实例。

正确理解的关键是不要再相信变量是引用类型还是值类型会影响存储分配位置的神话。那不是真的,从来都不是真的,甚至没有任何意义。

影响变量存储位置的唯一因素是变量的生存时间。短期变量在临时池(堆栈或寄存器)之外分配,而长期变量则在长期存储池(堆)之外分配。

【讨论】:

嗨,埃里克,感谢您的解释。但是您回答中的某些观点使我更加困惑。您能否解释一下我们如何区分短期变量和长期变量。如果您有任何文章或链接解释了相同的详细事实,请提出! @user373083:您可以像这样区分短期变量和长期变量:局部变量(包括临时变量)(1)不在迭代器块中,(2)不在外部变量中任何闭包都是短暂的变量。如果结构存储在短期变量中,则结构的字段是短期的。所有其他变量都是长期存在的。 @user:记住,你从来没有真正需要使用堆栈;这只是一个优化。有关编写不使用堆栈作为变量或返回地址的程序的方法,请参阅 Google Eric 的关于继续传递样式的系列。如果这种风格被融入到 JIT 中,那么它以这种方式生成所有方法的代码,我们根本就不会使用堆栈!这将使线程之类的东西便宜得多,启用真正的延续(这在当前的 CLR 中是不切实际的)等等。但是我们不会有一个堆栈,这对于调试和生成代码的简单性都非常有用。 (续)我的观点是:如果我们实现一个支持 CPS 的 CLR 和 JIT,就不会有堆栈。所有当前的 CLR 程序仍然同样有效。这就是为什么它只是一个实现细节。 @configurator:或者,可能有 两个 堆栈——一个用于存储局部变量,一个用于存储返回地址。每个线程两个堆栈的方法可以更容易地编写不成为堆栈粉碎攻击的牺牲品的不安全代码。本质上,堆栈或 CPS 都是实现 函数激活 概念的两种方式;有很多方法可以实现这个概念。使用单个堆栈恰好是一种既便宜又简单的方法,所以我们就是这样使用的。【参考方案2】:

这就是为什么 Eric Lippert 提醒我们the stack is an implementation detail.

当值类型的实例是引用类型的成员时,是的,它与父对象一起存储在托管堆中。这是一个很好的问题,你应该理解,而不是应该在大多数场景中推动你的设计。

structs 应该是小而简单的数据类型,创建和传递相对便宜。引用类型是您的复杂类型,只需要引用的副本即可传递给方法,但由于在堆上分配,因此当然会带来一些包袱。这是一个很好的follow up post,关于堆栈与堆分配的含义。

有很多参考资料解释了值类型与引用类型的性能影响。您应该全面了解它,并了解大多数情况下,这是一个语义决定,而不是性能决定。

【讨论】:

【参考方案3】:

那么当值类型变量(例如 int i =4;)在引用类型中(例如在类中)声明时,内存是如何分配的。

如果对象位于堆上,则意味着它的所有成员变量都在堆中。

【讨论】:

【参考方案4】:

这是一个不错的article。

顺便说一句:并非总是在堆栈中出现值 - 它可能在堆中结束。

【讨论】:

感谢文章链接。我认为文章简单有效。

以上是关于类中声明的变量的内存分配的主要内容,如果未能解决你的问题,请参考以下文章

为未初始化的std :: string变量分配了多少内存?

什么时候应该使用动态内存分配函数而不是直接变量声明?

声明变量以保存字符串列表时的内存分配

c语言是在声明变量的同时,分配内存的吗?程序结束运行了,内存会释放吗?

何时为变量分配内存,在声明时还是在初始化时?

何时在声明或初始化时为变量分配内存?