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

时间:2009-01-25 03:13:00

标签: c# .net performance stack heap

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

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

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

1问题已从SO中删除。

7 个答案:

答案 0 :(得分:16)

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

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

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

你的问题出现了,而Eric Lippert正在深入审查C# - 我有一节解释C#1中的内容,他认为developers shouldn't care

答案 1 :(得分:5)

编辑: 我的原始答案包含过度简化“结构在堆栈上分配”并且混淆了堆栈与vs-heap和value-vs-reference关注点,因为它们是耦合在C#。

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

public class Foo
{
   public int X = 0;
}

现在考虑以下代码:

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

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

将内容放入堆栈的原因之一是垃圾收集器不必清理它。你通常不应该担心这些事情;只是使用课程!现代垃圾收集器做得很好。一些现代虚拟机(如java 1.6)甚至可以determine whether it is safe to allocate objects on the stack,即使它们不是值类型。

答案 2 :(得分:3)

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

答案 3 :(得分:3)

在.NET中几乎没有什么可讨论的,因为不是某个类型的用户决定在哪里分配实例。

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

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

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

但是,由于堆是垃圾收集而堆栈不是,显然你会在某些情况下看到一些差异,但鉴于你在.NET中没有真正的选择,它几乎不相关。

答案 4 :(得分:3)

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

答案 5 :(得分:2)

与流行的看法相反,.NET进程中的堆栈和堆之间没有太大区别。堆栈和堆不过是虚拟内存中的地址范围,与为托管堆保留的地址范围相比,在保留给特定线程的堆栈的地址范围方面没有固有的优势。 访问堆上的内存位置并不比访问堆栈上的内存位置快或慢。在某些情况下,有一些考虑因素可能支持这样的说法:对堆栈位置的内存访问速度更快,总的来说,比访问堆位置的内存要多。其中:

  1. 在堆栈上,时间分配局部性(在时间上靠近在一起的分配)表示空间局部性(在空间上靠近在一起的存储)。反过来,当时间分配局部性表示时间访问局部性(一起分配的对象一起访问)时,顺序堆栈存储在CPU缓存和操作系统分页系统方面往往表现更好。
  2. 由于引用类型的开销,堆栈上的内存密度往往高于堆上的内存密度。更高的内存密度通常会带来更好的性能,例如,因为CPU缓存中可以容纳更多的对象。
  3. 线程堆栈通常会很小-Windows上默认的最大堆栈大小为1MB,大多数线程实际上实际上仅使用几个堆栈页面。在现代系统上,所有应用程序线程的堆栈都可以放入CPU缓存中,从而使典型的堆栈对象访问变得非常快。 (另一方面,整个堆很少适合CPU缓存。)
  

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

答案 6 :(得分:0)

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

类似的表现

  • 小型应用程序(堆上没有太多对象)
  • <1KB大小的对象

堆栈性能更高(快1到5倍)

  • 对象大小从1Kb到100KB

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

  • 大量对象
  • 大内存保证-每秒分配大量内存和全部内存
  • 大对象10KB-3MB(我想是x64系统)
  • XBox(慢速垃圾收集器)

最好的方法是数组池。它像堆栈一样快,但是您没有像堆栈这样的限制。

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

x64 Windows上堆栈的默认内存限制为4MB。因此您可以安全地分配不超过3MB的内存。