内存分配:堆栈与堆?

Mri*_*nal 79 .net c# heap stack

我对Stack vs Heap之间的内存分配基础感到困惑.根据标准定义(每个人都说的东西),所有值类型都将被分配到堆栈,参考类型将进入.

现在考虑以下示例:

class MyClass
{
    int myInt = 0;    
    string myString = "Something";
}

class Program
{
    static void Main(string[] args)
    {
       MyClass m = new MyClass();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,内存分配将如何在c#中发生?MyClass(即m)的对象是否会完全分配给堆?也就是说,int myIntstring myString既会去堆?

或者,该对象将分为两部分,并将分配给两个内存位置,即堆栈和堆?

Gab*_*abe 63

你应该考虑的问题,其中的物体得到分配为实现细节.对您来说,确切地存储对象位的位置并不重要.对象是引用类型还是值类型可能很重要,但在开始必须优化垃圾收集行为之前,您不必担心它将存储在何处.

虽然引用类型总是在当前实现中在堆上分配,但值类型可以在堆栈上分配 - 但不一定.仅当值类型是未装箱的非转义本地或临时变量(未包含在引用类型中且未在寄存器中分配)时,才会在堆栈上分配值类型.

  • 如果值类型是类的一部分(如在您的示例中),它将最终在堆上.
  • 如果它是盒装的,它将最终在堆上.
  • 如果它在一个数组中,它将最终在堆上.
  • 如果它是一个静态变量,它将最终在堆上.
  • 如果它被一个闭包捕获,它将最终在堆上.
  • 如果它在迭代器或异步块中使用,它将最终在堆上.
  • 如果它是由不安全或非托管代码创建的,则可以在任何类型的数据结构(不一定是堆栈或堆)中分配它.

我错过了什么吗?

当然,如果我没有链接到Eric Lippert关于这个话题的帖子,我会失职:

  • 您错过的情况是:如果值类型来自通过不安全的pointerthen访问的非托管代码,则它可能既不在堆栈也不在托管堆上.它可能位于非托管堆上,也可能位于某些甚至不是堆的数据结构中.有"堆"的整个想法也是一个神话.可能有几十堆.此外,如果抖动选择注册该值,则它不在堆栈或堆上,它位于寄存器中. (14认同)
  • 这很重要,因为它在面试中被问到,但在现实生活中却没有。:) (2认同)

Mud*_*Mud 53

m在堆上分配,包括myInt.在堆栈上分配基本类型(和结构)的情况是在方法调用期间,它为堆栈上的局部变量分配空间(因为它更快).例如:

class MyClass
{
    int myInt = 0;

    string myString = "Something";

    void Foo(int x, int y) {
       int rv = x + y + myInt;
       myInt = 2^rv;
    }
}
Run Code Online (Sandbox Code Playgroud)

rv,x,y都将在堆栈中.myInt在堆上的某个地方(必须通过this指针访问).

  • 一个重要的补充是要记住"堆栈"和"堆"实际上是.NET中的实现细节.完全可以创建一个C#的合法实现,它根本不使用基于堆栈的分配. (7认同)
  • 我同意他们应该以这种方式对待*,但它们纯粹是实现细节并不完全正确.它在公共API文档和语言标准(EMCA-334,ISO/IEC 23270:2006)中明确指出(即"结构值存储在堆栈中".谨慎的程序员有时可以通过明智地使用结构来提高性能. ")但是,是的,如果堆分配的速度是你的应用程序的瓶颈,你可能做错了(或使用错误的语言). (5认同)

Mar*_*ell 21

"所有VALUE类型都将分配给Stack"是非常非常错误的; struct变量可以作为方法变量存在于堆栈中.但是,类型上的字段与该类型一起使用.如果字段的声明类型是类,则值作为该对象的一部分在堆上.如果字段的声明类型是结构,则字段是结构所在的结构的一部分.

甚至方法变量可以在堆上,如果它们被捕获(lambda/anon-method),或者是(例如)迭代器块的一部分.