内存分配:堆栈与堆?

时间:2010-12-20 06:02:25

标签: c# .net stack heap

我对堆栈与堆之间的内存分配基础感到困惑。根据标准定义(每个人都说的话),所有值类型将被分配到堆栈参考类型将进入即可。

现在考虑以下示例:

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

class Program
{
    static void Main(string[] args)
    {
       MyClass m = new MyClass();
    }
}

现在,如何在c#中进行内存分配? MyClass的对象(即m)是否会完全分配给堆?也就是说,int myIntstring myString都会堆积?

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

9 个答案:

答案 0 :(得分:58)

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

虽然在当前实现中始终在堆上分配引用类型,但是值类型可能可以在堆栈上分配 - 但不一定如此。值类型仅在堆栈上分配时,它是未包装的非转义本地或临时变量,未包含在引用类型中且未在寄存器中分配。

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

我错过了什么吗?

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

答案 1 :(得分:52)

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

class MyClass
{
    int myInt = 0;

    string myString = "Something";

    void Foo(int x, int y) {
       int rv = x + y + myInt;
       myInt = 2^rv;
    }
}

rvxy都将在堆叠中。 myInt位于堆上的某个位置(必须通过this指针访问)。

答案 2 :(得分:20)

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

即使方法变量可以在堆上,如果它们是捕获(lambda / anon-method),或者是(例如)迭代器块的一部分。< / p>

答案 3 :(得分:10)

答案 4 :(得分:1)

简单的措施

值类型可以在THE STACK上进行,它是可以分配给某些未来主义者数据结构的实现细节。

所以,最好理解值和引用类型是如何工作的,值类型将被值复制,这​​意味着当您将值类型作为参数传递给FUNCTION时,它将被自然复制意味着您将拥有全新副本。

引用类型通过引用传递(againg不考虑引用将在某些未来版本中再次存储地址,它可能存储在其他一些数据结构中。)

所以在你的情况下

myInt是一个int,它在一个类中被封装在一个类中,它就是一个引用类型,因此它将被绑定到将存储在'THE HEAP'上的类的实例。

我建议,你可以开始阅读ERIC LIPPERTS写的博客。

Eric's Blog

答案 5 :(得分:1)

每次在其中创建对象时,都会进入称为堆的内存区域。原始变量就像 如果它们是本地方法变量,则在堆栈中分配int和double,如果它们是成员,则在堆中分配 变量。在方法中,当调用方法时,局部变量被推入堆栈 当方法调用完成时,堆栈指针递减。在每个线程的多线程应用程序中 将拥有自己的堆栈,但将共享相同的堆。这就是为什么要在代码中注意避免任何问题 堆空间中的并发访问问题。堆栈是线程安全的(每个线程都有自己的堆栈)但是 除非通过代码保护同步,否则堆不是线程安全的。

此链接也很有用http://www.programmerinterview.com/index.php/data-structures/difference-between-stack-and-heap/

答案 6 :(得分:1)

堆栈

  

stack是用于存储local variablesparameters的内存块。当输入和退出函数时,堆栈在逻辑上增长和缩小。

考虑以下方法:

public static int Factorial (int x)
{
    if (x == 0) 
    {
        return 1;
    }

    return x * Factorial (x - 1);
}

此方法是递归的,这意味着它会调用自身。 每次输入方法时,都会在堆栈上分配新的int ,每次方法退出时,int都会被释放

  
      
  • 堆是objects(即reference-type instances)所在的内存块。每当创建一个新对象时,它就会在堆上分配,并返回对该对象的引用。在程序执行期间,堆会在创建新对象时开始填充。运行时有一个垃圾收集器,它定期从堆中释放对象,因此您的程序不会运行Out Of Memory。一旦某个对象本身alive没有被引用,就有资格取消分配。
  •   
  • 堆还存储static fields。与堆上分配的对象(可以进行垃圾收集)不同,these live until the application domain is torn down
  •   

考虑以下方法:

using System;
using System.Text;

class Test
{
    public static void Main()
    {
        StringBuilder ref1 = new StringBuilder ("object1");
        Console.WriteLine (ref1);
        // The StringBuilder referenced by ref1 is now eligible for GC.

        StringBuilder ref2 = new StringBuilder ("object2");
        StringBuilder ref3 = ref2;
        // The StringBuilder referenced by ref2 is NOT yet eligible for GC.
        Console.WriteLine (ref3); // object2
    }
}    

在上面的例子中,我们首先创建一个由变量ref1引用的StringBuilder对象,然后写出它的内容。然后,StringBuilder对象立即有资格进行垃圾回收,因为之后没有任何内容使用它。然后,我们创建另一个由变量ref2引用的StringBuilder,并将该引用复制到ref3。即使在该点之后没有使用ref2,ref3也会使相同的StringBuilder对象保持活动状态 - 确保在完成使用ref3之前它不符合收集条件。

  

值变量实例(和对象引用)存在于变量所在的任何位置   声明。如果实例被声明为类类型中的字段或数组元素,那么该实例将存在于堆上。

答案 7 :(得分:0)

m是对MyClass对象的引用,因此m存储在主线程的堆栈中,但MyClass的对象存储在堆中。因此myInt和myString存储在堆中。 请注意,m只是一个引用(内存的地址)并且位于主堆栈上。当m deallocated然后GC清除堆中的MyClass对象 有关更多详细信息,请阅读本文的所有四个部分 https://www.c-sharpcorner.com/article/C-Sharp-heaping-vs-stacking-in-net-part-i/

答案 8 :(得分:-1)

根据标准定义(每个人都说过的事情),所有值类型都将分配到堆栈中,而引用类型将进入堆中。

这是错误的。只有本地(在函数上下文中)值类型/值类型数组在堆栈上分配。其他所有内容都在堆上分配。