C# 为什么结构存储在堆栈上,而类存储在堆(.NET)上?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/815354/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why are structs stored on the stack while classes get stored on the heap(.NET)?
提问by ichiban
I know that one of the differences between classes and structs is that struct instances get stored on stack and class instances(objects) are stored on the heap.
我知道类和结构之间的区别之一是结构实例存储在堆栈上,而类实例(对象)存储在堆上。
Since classes and structs are very similar. Does anybody know the difference for this particular distinction?
因为类和结构非常相似。有人知道这种特殊区别的区别吗?
采纳答案by Marc Gravell
(edited to cover points in comments)
(编辑以涵盖评论中的要点)
To emphasise: there are differences and similarities between value-types and reference-types, but those differences have nothingto do with stack vs heap, and everything to do with copy-semantics vs reference-semantics. In particular, if we do:
强调:值类型和引用类型之间存在差异和相似之处,但这些差异与堆栈与堆无关,而与复制语义与引用语义有关。特别是,如果我们这样做:
Foo first = new Foo { Bar = 123 };
Foo second = first;
Then are "first" and "second" talking about the same copy of Foo
? or different copies? It just so happens that the stack is a convenient and efficient way of handlingvalue-types as variables. But that is an implementation detail.
那么“第一个”和“第二个”是指同一个副本Foo
吗?还是不同的副本?恰巧堆栈是将值类型作为变量处理的一种方便且有效的方式。但这是一个实现细节。
(end edit)
(结束编辑)
Re the whole "value types go on the stack" thing... - value types don'talways go on the stack;
关于整个“值类型进入堆栈”的事情...... - 值类型并不总是进入堆栈;
- if they are fields on a class
- if they are boxed
- if they are "captured variables"
- if they are in an iterator block
- 如果它们是类中的字段
- 如果它们被装箱
- 如果它们是“捕获的变量”
- 如果它们在迭代器块中
then they go on the heap (the last two are actually just exotic examples of the first)
然后他们继续堆(最后两个实际上只是第一个的奇特例子)
i.e.
IE
class Foo {
int i; // on the heap
}
static void Foo() {
int i = 0; // on the heap due to capture
// ...
Action act = delegate {Console.WriteLine(i);};
}
static IEnumerable<int> Foo() {
int i = 0; // on the heap to do iterator block
//
yield return i;
}
Additionally, Eric Lippert (as already noted) has an excellent blog entryon this subject
回答by mqp
It's useful in practice to be able to allocate memory on the stack for some purposes, since those allocations are very fast.
在实践中能够出于某些目的在堆栈上分配内存很有用,因为这些分配非常快。
However, it's worth noting that there's no fundamental guarantee that all structs will be placed on the stack. Eric Lippert recently wrote an interesting blog entryon this topic.
但是,值得注意的是,没有基本保证所有结构都将放置在堆栈上。Eric Lippert 最近就这个主题写了一篇有趣的博客文章。
回答by Dortz
I believe that whether or not to use stack or heap space is the main distinction between the two, perhaps this article will shed some light on your question: Csharp classes vs structs
我相信是否使用堆栈或堆空间是两者之间的主要区别,也许这篇文章会阐明你的问题:Csharp classes vs structs
回答by Burcu Dogan
Every process has a data block consists of two different allocatable memory segment. These are stack and heap. Stack is mostly serving as the program flow manager and saves local variables, parameters and returning pointers (in a case of returning from the current working function).
每个进程都有一个数据块,由两个不同的可分配内存段组成。这些是堆栈和堆。Stack 主要作为程序流程管理器,保存局部变量、参数和返回指针(在从当前工作函数返回的情况下)。
Classes are very complex and mostly very large types compared to value types like structs (or basic types -- ints, chars, etc.) Since stack allocation should be specialized on the efficiency of program flow, it is not serving an optimal environment to keep large objects.
与结构(或基本类型——int、chars 等)等值类型相比,类非常复杂,而且大多是非常大的类型。由于堆栈分配应该专门针对程序流的效率,因此无法提供最佳环境来保持大型物体。
Therefore, to greet both of the expectations, this seperated architecture came along.
因此,为了满足这两种期望,这种分离的架构出现了。
回答by Andomar
Value types go on the stack, reference types go on the heap. A struct is a value type.
值类型在栈上,引用类型在堆上。结构体是一种值类型。
There is no guaruantee about this in the specification though, so it might change in future releases:)
不过,规范中对此没有任何保证,因此在未来的版本中可能会发生变化:)
回答by Marco van de Voort
In some languages, like C++, objects are also value types.
在某些语言中,例如 C++,对象也是值类型。
To find an example for the opposite is harder, but under classic Pascal union structs could only be instantiated on the heap. (normal structs could be static)
找到相反的例子更难,但在经典的 Pascal 联合结构下只能在堆上实例化。(正常结构可能是静态的)
In short: this situation is a choice, not a hard law. Since C# (and Java before it) lack procedural underpinnings, one can ask themselves why it needs structures at all.
简而言之:这种情况是一种选择,而不是硬性规定。由于 C#(以及之前的 Java)缺乏过程基础,人们可以问自己为什么它需要结构。
The reason it is there, is probably a combination of needing it for external interfaces and to have a performant and tight complex (container-) type. One that is faster than class. And then it is better to make it a value type.
它存在的原因可能是外部接口需要它以及具有高性能和紧密复杂(容器)类型的组合。一种比课堂更快的。然后最好将其设为值类型。
回答by JP Alioto
How the compiler and run-time environment handle memory management has grown up over a long period of time. The stack memory v.s. heap memory allocation decision hada lot to do with what could be known at compile-time and what could be known at runtime. This was before managed run times.
编译器和运行时环境处理内存管理的方式已经发展了很长一段时间。堆栈内存VS堆内存分配的决定有很多做什么可以在编译时是已知的,哪些可以在运行时是已知的。这是在托管运行时间之前。
In general, the compiler has very good control of what's on the stack, it gets to decide what is cleaned up and when based on calling conventions. The heap on the other hand, was more like the wild west. The compiler did not have good control of when things came and went. By placing function arguments on the stack, the compiler is able to make a scope-- that scope can be controlled over the lifetime of the call. This is a natural place to put value types, because they are easy to control as opposed to reference types that can hand out memory locations (pointers) to just about anyone they want.
一般来说,编译器对堆栈上的内容有很好的控制,它可以根据调用约定决定清理什么以及何时清理。另一方面,堆更像是狂野的西部。编译器无法很好地控制事情的发生和发生。通过将函数参数放在堆栈上,编译器能够创建一个作用域——该作用域可以在调用的整个生命周期内进行控制。这是放置值类型的自然位置,因为它们易于控制,而引用类型可以将内存位置(指针)分发给他们想要的任何人。
Modern memory management changes a lot of this. The .NET runtime can take control of reference types and the managed heap through complex garbage collection and memory management algorithms. This is also a very, very deep subject.
现代内存管理改变了很多。.NET 运行时可以通过复杂的垃圾收集和内存管理算法来控制引用类型和托管堆。 这也是一个非常非常深刻的主题。
I recommend you check out some texts on compilers -- I grew up on Aho, so I recommend that. You can also learn a lot about the subject by reading Gosling.
我建议你查看一些关于编译器的文章——我是在 Aho 上长大的,所以我推荐. 您还可以通过阅读Gosling来了解有关该主题的很多信息。
回答by mP.
The main difference being that the heap may hold objects that live forever while something on the stack is temporary in that it will disappear when the enclosing callsite is exited. This is because when one enters a method it grows to hold local variables as well as the caller method. When the method exits (ab)normally eg return or because of exception each frame must be popped off the stack. Eventually the interested frame is popped and everything on it lost.
主要区别在于,堆中可能包含永远存在的对象,而堆栈中的某些内容是临时的,因为当退出封闭的调用站点时它会消失。这是因为当一个人进入一个方法时,它会增长以容纳局部变量以及调用者方法。当方法正常退出(ab)时,例如返回或由于异常,每个帧都必须从堆栈中弹出。最终,感兴趣的框架被弹出,其上的所有内容都丢失了。
回答by jrcalzada
Marc Gravell already explained wonderfully the difference regarding how value and reference types are copied which is the main differentiation between them.
Marc Gravell 已经很好地解释了关于如何复制值和引用类型的差异,这是它们之间的主要区别。
As to why value types are usually created on the stack, that's because the way they are copied allows it. The stack has some definite advantages over the heap in terms of performance, particularly because the compiler can calculate the exact position of a variable created in a certain block of code, which makes access faster.
至于为什么通常在堆栈上创建值类型,那是因为它们的复制方式允许。堆栈在性能方面比堆有一些明显的优势,特别是因为编译器可以计算在特定代码块中创建的变量的确切位置,这使得访问速度更快。
When you create a reference type you receive a reference to the actual object which exists in the heap. There is a small level of indirection whenever you interact with the object itself. These reference types cannot be created on the stack because the lifetime of values in the stack is determined, in great part, by the structure of your code. The function frame of a method call will be popped off the stack when the function returns, for example.
当您创建引用类型时,您会收到对存在于堆中的实际对象的引用。每当您与对象本身交互时,都会有少量的间接性。这些引用类型不能在堆栈上创建,因为堆栈中值的生命周期在很大程度上取决于您的代码结构。例如,当函数返回时,方法调用的函数框架将从堆栈中弹出。
With value types, however, their copy semantics allows the compiler, depending on where it was created, to place it in the stack. If you create a local variable that holds an instance of a struct in a method and then return it, a copy of it will be created, as Marc explained above. This means that the value can be safely placed in the stack, since the lifetime of the actual instance is tied to the method's function frame. Anytime you send it somewhere outside the current function a copy of it will be created, so it doesn't matter if you tie the existence of the original instance to the scope of the function. Along these lines, you can also see why value types that are captured by closures need to go in the heap: They outlive their scope because they must also be accessible from within the closure, which can be passed around freely.
然而,对于值类型,它们的复制语义允许编译器根据它的创建位置将其放置在堆栈中。如果您在方法中创建一个包含结构实例的局部变量,然后返回它,则会创建它的副本,如 Marc 上面所解释的。这意味着该值可以安全地放置在堆栈中,因为实际实例的生命周期与方法的函数框架相关联。任何时候您将它发送到当前函数之外的某个地方时,都会创建一个它的副本,因此如果您将原始实例的存在与函数的作用域联系起来并不重要。沿着这些思路,您还可以看到为什么由闭包捕获的值类型需要进入堆:它们比它们的作用域更长,因为它们也必须可以从闭包内部访问,可以自由传递。
If it were a reference type, then you wouldn't be returning a copy of the object, but rather a reference, which means the actual value must be stored somewhere else, otherwise, if you returned the reference and the object's lifetime was tied to the scope in which it was created, it would end up pointing to an empty space in memory.
如果它是一个引用类型,那么你不会返回对象的副本,而是一个引用,这意味着实际值必须存储在其他地方,否则,如果你返回引用并且对象的生命周期被绑定到在创建它的范围内,它最终会指向内存中的一个空白空间。
The distinction isn't really that "Value types go on the stack, reference types on the heap". The real point is that it's usually more efficient to access objects that live in the stack, so the compiler will try and place those values it can there. It simply turns out that value types, because of their copy semantics, fit the bill better than reference types.
区别并不是“值类型在堆栈上,引用类型在堆上”。真正的重点是访问存在于堆栈中的对象通常更有效,因此编译器会尝试将这些值放在那里。事实证明,值类型,因为它们的复制语义,比引用类型更符合要求。
回答by Eric Lippert
That's a great question; I did not cover it in the article that Marc Gravell linked to. Here's part two:
这是一个很好的问题;我没有在 Marc Gravell 链接的文章中介绍它。这是第二部分: