内存分配:堆栈与堆?

发布于 2024-10-08 13:51:08 字数 548 浏览 8 评论 0原文

我对堆栈与堆之间的内存分配基础知识感到困惑。根据标准定义(大家都说的),所有值类型将被分配到堆栈上,而引用类型将进入<强>堆。

现在考虑以下示例:

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

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

现在,c# 中的内存分配将如何发生? MyClass的对象(即m)会被完全分配到Heap吗?也就是说,int myIntstring myString都会进入堆?

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

I am getting confused with memory allocation basics between Stack vs Heap. As per the standard definition (things which everybody says), all Value Types will get allocated onto a Stack and Reference Types will go into the Heap.

Now consider the following example:

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

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

Now, how does the memory allocation will happen in c#? Will the object of MyClass (that is, m) will be completely allocated to the Heap? That is, int myInt and string myString both will go to heap?

Or, the object will be divided into two parts and will be allocated to both of the memory locations that is, Stack and Heap?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

掩耳倾听 2024-10-15 13:51:09

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

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

Each time an object is created in it goes into the area of memory known as heap. The primitive variables like
int and double are allocated in the stack, if they are local method variables and in the heap if they are member
variables . In methods local variables are pushed into stack when a method is invoked
and stack pointer is decremented when a method call is completed. In a multithreaded application each thread
will have its own stack but will share the same heap. This is why care should be taken in your code to avoid any
concurrent access issues in the heap space. The stack is threadsafe (each thread will have its own stack) but the
heap is not thread safe unless guarded with synchronisation through your code.

This link is also useful http://www.programmerinterview.com/index.php/data-structures/difference-between-stack-and-heap/

2024-10-15 13:51:09

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

m is a reference to an object of MyClass so m is stores in the stack of main thread but the object of MyClass stores in the heap. Therefore myInt and myString store in the heap.
Note that m is only a reference (an address to memory) and is on main stack. when m deallocated then GC clear the MyClass object from the heap
For more detail read all four parts of this article
https://www.c-sharpcorner.com/article/C-Sharp-heaping-vs-stacking-in-net-part-i/

度的依靠╰つ 2024-10-15 13:51:09

根据标准定义(大家都这么说),所有值类型都将分配到堆栈上,而引用类型将分配到堆中。

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

As per the standard definition (things which everybody says), all Value Types will get allocated onto a Stack and Reference Types will go into the Heap.

This is wrong. Only local (in the context of a function) value types/arrays of value types are allocated on the stack. Everything else is allocated on the heap.

垂暮老矣 2024-10-15 13:51:08

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

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

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

有什么我错过的吗?

当然,如果我没有链接到 Eric Lippert 关于该主题的帖子,那就是我的失职:

You should consider the question of where objects get allocated as an implementation detail. It does not matter to you exactly where the bits of an object are stored. It may matter whether an object is a reference type or a value type, but you don't have to worry about where it will be stored until you start having to optimize garbage collection behavior.

While reference types are always allocated on the heap in current implementations, value types may be allocated on the stack -- but aren't necessarily. A value type is only allocated on the stack when it is an unboxed non-escaping local or temporary variable that is not contained within a reference type and not allocated in a register.

  • If a value type is part of a class (as in your example), it will end up on the heap.
  • If it's boxed, it will end up on the heap.
  • If it's in an array, it will end up on the heap.
  • If it's a static variable, it will end up on the heap.
  • If it's captured by a closure, it will end up on the heap.
  • If it's used in an iterator or async block, it will end up on the heap.
  • If it's created by unsafe or unmanaged code, it could be allocated in any type of data structure (not necessarily a stack or a heap).

Is there anything I missed?

Of course, I would be remiss if I didn't link to Eric Lippert's posts on the topic:

如果没有你 2024-10-15 13:51:08

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 指针访问)。

m is allocated on the heap, and that includes myInt. The situations where primitive types (and structs) are allocated on the stack is during method invocation, which allocates room for local variables on the stack (because it's faster). For example:

class MyClass
{
    int myInt = 0;

    string myString = "Something";

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

rv, x, y will all be on the stack. myInt is somewhere on the heap (and must be access via the this pointer).

花想c 2024-10-15 13:51:08

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

即使方法变量也可以位于堆上,如果它们被捕获(lambda/匿名方法),或者是(例如)迭代器块的一部分。

"All VALUE Types will get allocated to Stack" is very, very wrong; struct variables can live on the stack, as method variables. However, fields on a type live with that type. If a field's declaring type is a class, the values are on the heap as part of that object. If a field's declaring type is a struct, the fields are part of that struct where-ever that struct lives.

Even method variables can be on the heap, if they are captured (lambda/anon-method), or part of (for example) an iterator block.

心房的律动 2024-10-15 13:51:08

堆栈是用于存储局部变量参数的内存块。随着函数的进入和退出,堆栈逻辑上会增长和收缩。

考虑以下方法:

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

    return x * Factorial (x - 1);
}

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


  • 堆是对象(即引用类型实例)所在的内存块。每当创建一个新对象时,都会在堆上分配它,并返回对该对象的引用。在程序执行期间,随着新对象的创建,堆开始填满。运行时有一个垃圾收集器,它会定期从堆中释放对象,因此您的程序不会运行内存不足。只要对象没有被任何活动引用,该对象就可以被释放。
  • 堆还存储静态字段。与分配在堆上的对象(可以被垃圾回收)不同,这些对象会一直存在,直到应用程序域被拆除

考虑以下方法:

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 之前它不会变得符合收集条件。

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

Stack

The stack is a block of memory for storing local variables and parameters. The stack logically grows and shrinks as a function is entered and exited.

Consider the following method:

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

    return x * Factorial (x - 1);
}

This method is recursive, meaning that it calls itself. Each time the method is entered, a new int is allocated on the stack, and each time the method exits, the int is deallocated.


Heap

  • The heap is a block of memory in which objects (i.e., reference-type instances) reside. Whenever a new object is created, it is allocated on the heap, and a reference to that object is returned. During a program’s execution, the heap starts filling up as new objects are created. The runtime has a garbage collector that periodically deallocates objects from the heap, so your program does not run Out Of Memory. An object is eligible for deallocation as soon as it’s not referenced by anything that’s itself alive.
  • The heap also stores static fields. Unlike objects allocated on the heap (which can get garbage-collected), these live until the application domain is torn down.

Consider the following method:

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
    }
}    

In the above example, we start by creating a StringBuilder object referenced by the variable ref1, and then write out its content. That StringBuilder object is then immediately eligible for garbage collection, because nothing subsequently uses it. Then, we create another StringBuilder referenced by variable ref2, and copy that reference to ref3. Even though ref2 is not used after that point, ref3 keeps the same StringBuilder object alive—ensuring that it doesn’t become eligible for collection until we’ve finished using ref3.

Value-type instances (and object references) live wherever the variable was
declared. If the instance was declared as a field within a class type, or as an array element, that instance lives on the heap.

混吃等死 2024-10-15 13:51:08

简单的措施

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

因此,最好了解值和引用类型的工作原理,值类型将按值复制,这​​意味着当您将值类型作为参数传递给 FUNCTION 时,它会自然复制,这意味着您将拥有一个全新的副本。

引用类型是通过引用传递的(同样不要考虑引用会在未来的版本中再次存储地址,它可能存储在其他一些数据结构上。)

所以在你的情况下

myInt 是一个 int ,它被封装在一个类中,该类当然是一个引用类型,因此它将与存储在“THE HEAP”中的类的实例绑定。

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

Eric 的博客 < /a>

simple measures

Value type can be stred on THE STACK ,it is the implementaional detail it can be allocated to the some futuristist data structure.

so, it is better to understand how value and reference type works , Value type will be copied by value that means when you pass a value type as a param to a FUNCTION than it will be copied by nature means you will have a total new copy.

Reference types are passed by reference ( againg do not consider reference will store a address again in some future versions ,it may be stored on some other data structures.)

so in your case

myInt is a int which is ecapsulated in a class which offcourse an reference type so it will be tied to the instance of the class which will be stored on 'THE HEAP'.

i would suggest , you can start reading blogs written by ERIC LIPPERTS.

Eric's Blog

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文