在我看来,可以用堆栈完成的所有事情都可以用堆来完成,但是不能用堆栈完成的所有事情都可以用堆栈来完成。那是对的吗?然后,为了简单起见,即使我们在某些工作负载下确实损失了少量性能,仅采用一个标准(即堆)难道不是更好吗?
考虑一下模块化和性能之间的权衡。我知道这不是描述这种情况的最佳方法,但是总的来说,即使有可能获得更好的性能,理解和设计的简单性似乎也是一个更好的选择。
在我看来,可以用堆栈完成的所有事情都可以用堆来完成,但是不能用堆栈完成的所有事情都可以用堆栈来完成。那是对的吗?然后,为了简单起见,即使我们在某些工作负载下确实损失了少量性能,仅采用一个标准(即堆)难道不是更好吗?
考虑一下模块化和性能之间的权衡。我知道这不是描述这种情况的最佳方法,但是总的来说,即使有可能获得更好的性能,理解和设计的简单性似乎也是一个更好的选择。
Answers:
堆不利于快速分配和释放内存。如果您想在有限的时间内获取少量内存,那么堆并不是您的最佳选择。具有超简单分配/释放算法的堆栈自然在此方面表现出色(如果将其内置到硬件中则更是如此),这就是为什么人们将其用于诸如将参数传递给函数和存储局部变量之类的原因。重要的缺点是它的空间有限,因此在其中保留大型对象或尝试将其用于寿命长的对象都是不好的主意。
为了简化编程语言而完全摆脱堆栈是IMO的错误方法-更好的方法是将差异抽象化,让编译器找出要使用的存储类型,而程序员将更高的存储在一起-与人类思维方式更接近的层次结构-实际上,诸如C#,Java,Python等高级语言正是这样做的。它们为堆分配的对象和堆栈分配的原语(.NET术语中的“引用类型”与“值类型”)提供几乎完全相同的语法,这些语法完全透明,或者您必须理解一些功能差异才能使用该语言。正确(但是您实际上不必知道堆栈和堆在内部如何工作)。
没有
相比较而言,C ++中的堆栈区域快得令人难以置信。我敢冒险,没有经验的C ++开发人员会愿意禁用该功能。
使用C ++,您可以选择并拥有控制权。设计师并不特别倾向于引入会增加执行时间或空间的功能。
行使选择
如果要构建要求动态分配每个对象的库或程序,则可以使用C ++进行。它的执行速度相对较慢,但是您可以拥有这种“模块化”。对于我们其他人来说,模块化始终是可选的,请根据需要对其进行介绍,因为良好/快速的实现都需要模块化。
备择方案
还有其他语言要求在堆上创建每个对象的存储。它非常慢,以至于损害设计(现实世界的程序)的方式比必须学习两者(IMO)更糟糕。
两者都很重要,C ++可以有效地为每种给定场景提供电源。话虽如此,但如果OP中的这些因素对您很重要(例如,请阅读高级语言),则C ++语言可能不是您设计的理想选择。
然后,为了简单起见,即使我们在某些工作负载下确实损失了少量性能,仅采用一个标准(即堆)难道不是更好吗?
实际上,性能损失可能很大!
正如其他人指出的那样,堆栈是一种非常有效的结构,用于管理遵守LIFO(后进先出)规则的数据。堆栈上的内存分配/释放通常只是对CPU上的寄存器的更改。更改寄存器几乎始终是处理器可以执行的最快的操作之一。
堆通常是一个相当复杂的数据结构,分配/释放内存将花费很多指令来完成所有相关的簿记工作。更糟糕的是,在常见的实现中,每次使用堆的调用都可能导致对操作系统的调用。操作系统调用非常耗时!该程序通常必须从用户模式切换到内核模式,并且每当发生这种情况时,操作系统可能会决定其他程序有更紧迫的需求,并且您的程序将需要等待。
Simula将堆用于所有内容。将所有内容放到堆上总是会引起局部变量的间接访问,这给垃圾收集器带来了额外的压力(您必须考虑到垃圾收集器当时确实很烂)。这就是Bjarne发明C ++的部分原因。
就您编写代码而言,“高效”可能会,但就软件效率而言,肯定不会。堆栈分配基本上是免费的(只需几条机器指令即可移动堆栈指针并在堆栈上为局部变量保留空间)。
由于堆栈分配几乎不需要时间,因此即使在非常高效的堆上进行分配也要慢100k倍(如果不是1M +)。
现在,想象一个典型应用程序使用多少个局部变量和其他数据结构。用作循环计数器的每个小“ i”的分配速度要慢一百万倍。
确保硬件速度足够快,您可以编写仅使用堆的应用程序。但是现在想像一下,如果您利用堆并使用相同的硬件,则可以编写什么样的应用程序。
您可能对“垃圾回收快,但堆栈快”感兴趣。
http://dspace.mit.edu/bitstream/handle/1721.1/6622/AIM-1462.ps.Z
如果我没看错的话,这些家伙会修改C编译器以在堆上分配“堆栈帧”,然后使用垃圾回收来取消分配帧,而不是弹出堆栈。
堆栈分配的“堆栈框架”在决定性方面胜过堆分配的“堆栈框架”。
调用堆栈如何在堆上工作?本质上,您将必须在每个程序的堆上分配一个堆栈,那么为什么不让OS +硬件为您这样做呢?
如果您希望事情真的非常简单和高效,只需给用户分配他们的内存块,然后让他们处理。当然,没有人愿意自己实现一切,这就是为什么我们有一个堆栈和一个堆。
堆栈和堆都是必需的。它们用于不同的情况,例如:
基本上根本无法比较这些机制,因为很多细节都不同。它们唯一的共同之处在于它们都以某种方式处理内存。
除了大型但速度较慢的主内存系统之外,现代计算机还具有几层缓存。在从主存储系统读取或写入一个字节所需的时间内,人们可以对最快的高速缓存进行数十次访问。因此,访问一个位置一千次要比访问每个位置1,000个(甚至100个)独立位置快得多。由于大多数应用程序会在堆栈顶部附近重复分配和释放少量内存,因此堆栈顶部的位置会被大量使用和重复使用,因此绝大多数(典型应用程序中超过99%)堆栈访问可以使用高速缓存来处理。
相比之下,如果应用程序要重复创建和放弃堆对象以存储连续信息,则曾经创建的每个堆栈对象的每个版本都必须写到主存储器中。即使在CPU想要回收它们开始的缓存页面时,绝大多数此类对象将完全无用,CPU也无法得知。因此,CPU将不得不浪费大量时间执行对无用信息的慢速存储器写入。不完全是提高速度的秘诀。
要考虑的另一件事是,在许多情况下,知道一旦例程退出就不会使用传递给例程的对象引用是很有用的。如果参数和局部变量是通过堆栈传递的,并且对例程代码的检查表明它没有持久保存传入引用的副本,则调用例程的代码可以确保如果没有外部引用,对象在调用之前存在,之后将不存在。相比之下,如果参数是通过堆对象传递的,则“例程返回之后”之类的概念会变得更加模糊,因为如果代码保留了延续的副本,则例程可能会在返回a之后多次“返回”一次通话。