简体   繁体   English

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

[英]Heap versus Stack allocation implications (.NET)

From an SO answer 1 about Heap and Stack, it raised me a question: Why it is important to know where the variables are allocated? 从关于Heap和Stack的 SO回答 1中 ,它提出了一个问题:为什么知道变量的分配位置很重要?

At another answer someone 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中删除。

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 could notice that a newly created object was never used outside the current method (the reference could never escape elsewhere) and allocate it on the stack. 例如,JIT 可能会注意到新创建的对象从未在当前方法之外使用(引用永远不能在其他地方转义)并在堆栈上分配它。 It doesn't do that at the moment, but it would be a legal thing to do. 目前它不会这样做,但这是合法的事情。

Likewise the C# compiler could decide 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. 同样,C#编译器可以决定在堆上分配所有局部变量 - 堆栈只包含对MyMethodLocalVariables实例的引用,并且所有变量访问都将通过它实现。 (In fact, variables captured by delegates or iterator blocks already have this sort of behaviour.) (事实上​​,委托或迭代器块捕获的变量已经具有这种行为。)

( 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#. ) 编辑: 我的原始答案包含过度简化“结构在堆栈上分配”和混淆堆栈与vs-heap和值与引用的关系,因为它们在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. 在此示例中,foo和foo2是对同一对象的引用。 Setting X on foo2 will also affect foo1. 在foo2上设置X也会影响foo1。 If we change the Foo class to a struct then this is no longer the case . 如果我们将Foo类更改为结构,则不再是这种情况 This is because structs are not accessed through references. 这是因为结构不能通过引用访问。 Assigning foo2 will actually make a copy. 分配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 stack even if they are not value types. 一些现代虚拟机(如java 1.6)甚至可以确定在堆栈上分配对象是否安全,即使它们不是值类型。

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. 例外情况是值类型是引用类型的一部分,在这种情况下,它与引用类型一起分配在堆上。 Ie 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++. 这与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. 事实上,在.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中没有真正的选择,它几乎不相关。

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. 当在堆栈上时,变量将被使用它的任何东西解除,例如实例化它的方法。

Contrary to popular belief, there isn't that much of a difference between stacks and heaps in a .NET process. 与流行的看法相反,.NET进程中的堆栈和堆栈之间没有太大区别。 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: 其中:

  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. 反过来,当时间分配局部性意味着时间访问局部性(一起分配的对象被一起访问)时,顺序堆栈存储倾向于相对于CPU高速缓存和操作系统分页系统执行得更好。
  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, eg, because more objects fit in the CPU cache. 较高的内存密度通常会带来更好的性能,例如,因为更多的对象适合CPU缓存。
  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. 线程堆栈往往相当小 - Windows上的默认最大堆栈大小为1MB,大多数线程实际上只使用少量堆栈页面。 On modern systems,the stacks of all application threads can fit into the CPU cache, making typical stack object access extremely fast. 在现代系统中,所有应用程序线程的堆栈都可以放入CPU缓存中,从而使典型的堆栈对象访问速度极快。 (Entire heaps, on the other hand, rarely fit into CPU caches.) (另一方面,整个堆很少适合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上的线程堆栈是有限的,通过应用不明智的递归和大堆栈分配很容易耗尽堆栈。

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? 以下问题使得理解差异变得至关重要:您认为.NET访问的速度更快,效率更高? - Stack or Heap. - 堆叠或堆。 In what scenarios .NET may place a value type of the heap? 在什么情况下.NET可以放置堆的值类型?

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) Sligther更好的堆栈性能 (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) 大对象10KB - 3MB(我想x64系统)
  • XBox (slow garbage collector) 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. x64 Windows上堆栈的默认内存限制为4MB。 So you will be safe with allocating not more than 3MB. 因此,分配不超过3MB的安全性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM