从关于堆和堆栈的 SO answer 1 ,它提出了一个问题:为什么知道变量的分配位置很重要?
在another answer有人指出堆栈更快。这是唯一的含义吗?有人可以提供一个代码示例,其中简单的分配位置更改可以解决问题(例如,性能)?
请注意,此问题是特定于.NET的
1问题已从SO中删除。
答案 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进程中的堆栈和堆之间没有太大区别。堆栈和堆不过是虚拟内存中的地址范围,与为托管堆保留的地址范围相比,在保留给特定线程的堆栈的地址范围方面没有固有的优势。 访问堆上的内存位置并不比访问堆栈上的内存位置快或慢。在某些情况下,有一些考虑因素可能支持这样的说法:对堆栈位置的内存访问速度更快,总的来说,比访问堆位置的内存要多。其中:
话虽如此,您不应该将所有分配转移到 堆! Windows上的线程堆栈有限,并且很容易耗尽 通过应用有害的递归和大堆栈 分配。
答案 6 :(得分:0)
我在堆栈和堆上使用了不同的基准测试,因此可以得出以下结论:
类似的表现
堆栈性能更高(快1到5倍)
更好的性能(快100倍甚至更多)
最好的方法是数组池。它像堆栈一样快,但是您没有像堆栈这样的限制。
使用堆栈的另一个含义是通过设计它是线程安全的。
x64 Windows上堆栈的默认内存限制为4MB。因此您可以安全地分配不超过3MB的内存。