C# 堆与堆栈分配的影响 (.NET)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/477101/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 04:56:00  来源:igfitidea点击:

Heap versus Stack allocation implications (.NET)

c#.netperformancestackheap

提问by Jader Dias

From an SO answer1about Heap and Stack, it raised me a question: Why it is important to know where the variables are allocated?

从关于堆和堆栈的SO 答案1 中,它向我提出了一个问题:为什么知道变量的分配位置很重要?

At another answersomeone pointed that the stack is faster. Is this the only implication? Could someone give a code example where a simple allocation location change could solve a problem (eg. performance)?

另一个答案中,有人指出堆栈更快。这是唯一的含义吗?有人可以给出一个简单的分配位置更改可以解决问题(例如性能)的代码示例吗?

Note that this question is .NET specific

请注意,此问题是特定于 .NET 的

1 the question is removed from SO.

1 该问题已从 SO 中删除。

采纳答案by Jon Skeet

So long as you know what the semantics are, the only consequences of stack vs heap are in terms of making sure you don't overflow the stack, and being aware that there's a cost associated with garbage collecting the heap.

只要您知道语义是什么,堆栈与堆的唯一后果就是确保您不会溢出堆栈,并意识到与垃圾收集堆相关的成本。

For instance, the JIT couldnotice that a newly created object was never used outside the current method (the reference could never escape elsewhere) and allocate it on the stack. It doesn't do that at the moment, but it would be a legal thing to do.

例如,JIT可以注意到在当前方法之外从未使用过新创建的对象(引用永远不会在其他地方转义)并将其分配到堆栈上。它目前不这样做,但这样做是合法的。

Likewise the C# compiler coulddecide to allocate all local variables on the heap - the stack would just contain a reference to an instance of MyMethodLocalVariables and all variable access would be implemented via that. (In fact, variables captured by delegates or iterator blocks already have this sort of behaviour.)

同样,C# 编译器可以决定在堆上分配所有局部变量 - 堆栈将只包含对 MyMethodLocalVariables 实例的引用,并且所有变量访问都将通过它实现。(实际上,委托或迭代器块捕获的变量已经具有这种行为。)

回答by DavGarcia

I think the simplest reason is that if it is in the Heap the the garbage collection needs to deal with the variable once it is no longer needed. When on a Stack, the variable is dismissed with whatever was using it, such as a method that instantiated it.

我认为最简单的原因是,如果它在堆中,垃圾收集需要处理不再需要的变量。当在堆栈上时,变量会被使用它的任何东西解除,例如实例化它的方法。

回答by Wim Coenen

(edit:My original answer contained the oversimplification "structs are allocated on the stack" and confused stack-vs-heap and value-vs-reference concerns a bit, because they are coupled in C#.)

编辑:我的原始答案包含过度简化的“结构在堆栈上分配”并且混淆了堆栈与堆和值与引用的关系,因为它们在 C# 中耦合。

Whether objects live on the stack or not is an implementation detail which is not very important. Jon has already explained this well. When choosing between using a class and struct, it is more important to realize that reference types work differently than value types. Take the following simple class as an example:

对象是否存在于堆栈中是一个不太重要的实现细节。乔恩已经很好地解释了这一点。在使用类和结构之间进行选择时,更重要的是要意识到引用类型的工作方式与值类型不同。以下面这个简单的类为例:

public class Foo
{
   public int X = 0;
}

Now consider the following code:

现在考虑以下代码:

Foo foo = new Foo();
Foo foo2 = foo;
foo2.X = 1;

In this example, foo and foo2 are references to the same object. Setting X on foo2 will also affect foo1. If we change the Foo class to a struct then this is no longer the case. This is because structs are not accessed through references. Assigning foo2 will actually make a copy.

在这个例子中,foo 和 foo2 是对同一个对象的引用。在 foo2 上设置 X 也会影响 foo1。如果我们将 Foo 类更改为结构体,则情况不再如此。这是因为结构不是通过引用访问的。分配 foo2 实际上会复制一份。

One of the reasons for putting stuff on the stack is that the garbage collector does not have to clean it up. You typically should not worry about such things; just use classes! Modern garbage collectors do a pretty good job. Some modern virtual machines (like java 1.6) can even determine whether it is safe to allocate objects on the stackeven if they are not value types.

将东西放入堆栈的原因之一是垃圾收集器不必清理它。你通常不应该担心这些事情;只需使用课程!现代垃圾收集器做得很好。一些现代虚拟机(如 java 1.6)甚至可以确定在堆栈上分配对象是否安全,即使它们不是值类型。

回答by Brian Rasmussen

In .NET there's little to discuss as it is not the user of a type who decides where to allocate instances.

在 .NET 中,没有什么可讨论的,因为决定将实例分配到何处的不是类型的用户。

Reference types are always allocated on the heap. Value types are per default allocated on the stack. The exception is if the value type is part of a reference type in which case it is allocated on the heap along with the reference type. I.e. the designer of a type makes this decision on behalf of the users.

引用类型总是在堆上分配。值类型默认分配在堆栈上。例外情况是如果值类型是引用类型的一部分,在这种情况下,它与引用类型一起在堆上分配。即类型的设计者代表用户做出这个决定。

In languages such as C or C++ the user can decide where data is allocated and for some special cases it may be significantly faster to allocate from the stack compared to allocating from the heap.

在诸如 C 或 C++ 之类的语言中,用户可以决定数据的分配位置,对于某些特殊情况,与从堆分配相比,从堆栈分配可能要快得多。

This has to do with how heap allocations are handled for C / C++. In fact heap allocation is pretty fast in .NET (except when it triggers a garbage collect), so even if you could decide where to allocate, my guess is that the difference would not be significant.

这与 C/C++ 处理堆分配的方式有关。事实上,.NET 中的堆分配非常快(除非它触发垃圾收集),所以即使您可以决定分配的位置,我的猜测是差异不会很大。

However, since the heap is garbage collected and the stack is not, obviously you would see some differences in certain cases, but it is hardly relevant given the fact that you don't really have a choice in .NET.

但是,由于堆是垃圾收集的,而堆栈不是,显然在某些情况下您会看到一些差异,但考虑到您在 .NET 中实际上没有选择的事实,这几乎无关紧要。

回答by Ankit Dass

In my opinion knowing about the differences between the stack and heap and how things are allocated on it can be very helpful when you really start thinking about performance of your application. The following questions make it essential to understand the differences: What do you think is faster and more efficient for .NET to access? - Stack or Heap. In what scenarios .NET may place a value type of the heap?

在我看来,当您真正开始考虑应用程序的性能时,了解堆栈和堆之间的差异以及如何在其上分配内容会非常有帮助。以下问题使理解差异变得至关重要: 您认为 .NET 访问什么更快、更有效?- 堆栈或堆。.NET 在什么情况下可以放置堆的值类型?

回答by Ali Bayat

Contrary to popular belief, there isn't that much of a difference between stacks and heaps in a .NET process. Stacks and heaps are nothing more than ranges of addresses in virtual memory, and there is no inherent advantage in the range of addresses reserved to the stack of a particular thread compared to the range of addresses reserved for the managed heap. Accessing a memory location on the heap is neither faster nor slower than accessing a memory location on the stack.There are several considerations that might,in certain cases, support the claim that memory access to stack locations is faster, overall, than memory access to heap locations. Among them:

与流行的看法相反,.NET 进程中的堆栈和堆之间没有太大区别。堆栈和堆只不过是虚拟内存中的地址范围,与为托管堆保留的地址范围相比,为特定线程的堆栈保留的地址范围没有固有的优势。访问堆上的内存位置既不比访问堆栈上的内存位置快也不慢。在某些情况下,有几个考虑因素可能支持对堆栈位置的内存访问总体上比对堆位置的内存访问速度更快的说法。他们之中:

  1. On the stack, temporal allocation locality (allocations made close together in time) implies spatial locality (storage that is close together in space). In turn, when temporal allocation locality implies temporal access locality (objects allocated together are accessed together), the sequential stack storage tends to perform better with respect to CPU caches and operating system paging systems.
  2. Memory density on the stack tends to be higher than on the heap because of the reference type overhead. Higher memory density often leads to better performance, e.g., because more objects fit in the CPU cache.
  3. Thread stacks tend to be fairly small – the default maximum stack size on Windows is 1MB, and most threads tend to actually use only a few stack pages. On modern systems,the stacks of all application threads can fit into the CPU cache, making typical stack object access extremely fast. (Entire heaps, on the other hand, rarely fit into CPU caches.)
  1. 在堆栈上,时间分配局部性(在时间上靠得很近的分配)意味着空间局部性(在空间上靠得很近的存储)。反过来,当时间分配局部性意味着时间访问局部性(一起分配的对象被一起访问)时,顺序堆栈存储在 CPU 缓存和操作系统分页系统方面往往表现更好。
  2. 由于引用类型开销,堆栈上的内存密度往往高于堆上的内存密度。更高的内存密度通常会带来更好的性能,例如,因为更多的对象适合 CPU 缓存。
  3. 线程堆栈往往相当小——Windows 上的默认最大堆栈大小为 1MB,并且大多数线程实际上往往只使用几个堆栈页面。在现代系统上,所有应用程序线程的堆栈都可以放入 CPU 缓存中,从而使典型的堆栈对象访问速度极快。(另一方面,整个堆很少适合 CPU 缓存。)

With that said, you should not be moving all your allocations to the stack! Thread stacks on Windows are limited, and it is easy to exhaust the stack by applying injudicious recursion and large stack allocations.

话虽如此,您不应该将所有分配移到堆栈中!Windows 上的线程堆栈是有限的,并且通过应用不明智的递归和大量堆栈分配很容易耗尽堆栈。

回答by Tomas Kubes

I played a lot with a different benchmarks on stack and heap and I would conclude in following:

我在堆栈和堆上使用不同的基准测试了很多,我会得出以下结论:

Similar performance for

类似的表现

  • small apps (not too many objects on heap)
  • objects of size < 1KB
  • 小应用程序(堆上的对象不要太多)
  • 大小小于 1KB 的对象

Sligther better stack performance for(1x - 5x faster)

Sligter 更好的堆栈性能(1x - 5x 快)

  • Objects of size 1Kb to 100KB
  • 大小为 1Kb 到 100KB 的对象

Much better performance for(up to 100x faster or even more)

更好的性能(最多快 100 倍甚至更多)

  • Large number of objects
  • Big memory presure - a lot allocations per seconds and full memory
  • large objects 10KB - 3MB (I suppose x64 systems)
  • XBox (slow garbage collector)
  • 大量对象
  • 大内存压力 - 每秒大量分配和完整内存
  • 大对象 10KB - 3MB(我想 x64 系统)
  • XBox(慢速垃圾收集器)

The best aproach is array pooling. It is fast as stack, but you don't have such limitation like with stack.

最好的方法是阵列池。它与堆栈一样快,但您没有像堆栈那样的限制。

Another implication of using stack is that it is thread-safe by desing.

使用堆栈的另一个含义是通过设计它是线程安全的。

Default memory limit for stack on x64 Windows is 4MB. So you will be safe with allocating not more than 3MB.

x64 Windows 上堆栈的默认内存限制为 4MB。因此,分配不超过 3MB 的空间是安全的。