为什么堆栈内存大小如此有限?

在堆上分配内存时,唯一的限制是可用RAM(或虚拟内存)。 它使记忆的Gb。

那么为什么堆栈大小如此有限(大约1 Mb)呢? 什么技术原因阻止你在栈上创build真正的大对象?

更新 :我的意图可能不清楚,我不想在堆栈上分配巨大的对象,我不需要更大的堆栈。 这个问题只是纯粹的好奇心。

我的直觉是以下。 堆栈不像堆一样容易pipe理。 堆栈需要存储在连续的内存位置。 这意味着你不能根据需要随机分配堆栈,但你至less需要为此保留虚拟地址。 保留虚拟地址空间的大小越大,可以创build的线程越less。

例如,32位应用程序通常具有2GB的虚拟地址空间。 这意味着如果堆栈大小为2MB(在pthread中是默认值),则可以创build最多1024个线程。 这对于Web服务器等应用程序来说可能很小。 例如,将堆栈大小增加到100MB(即,您预留了100MB,但不一定将100MB立即分配给堆栈)将会将线程数限制在20个左右,即使对于简单的GUI应用程序也是如此。

一个有趣的问题是,为什么我们仍然在64位平台上有这个限制。 我不知道答案,但是我认为人们已经习惯了一些“堆栈最佳实践”:小心地在堆上分配巨大的对象,并在需要时手动增加堆栈大小。 因此,没有人发现在64位平台上添加“巨大的”堆栈支持是有用的。

尚未有人提及的一个方面:

有限的堆栈大小是错误检测和遏制机制。

一般来说,C和C ++中堆栈的主要工作是跟踪调用堆栈和局部variables,如果堆栈超出边界,那么在devise和/或应用程序的行为中几乎总是出错。

如果堆栈允许增长到任意大,那么只有在操作系统资源耗尽之后,这些错误(如无限recursion)才会被捕获得很晚。 这可以通过设置堆栈大小的任意限制来防止。 实际尺寸并不重要,除了足够小以防止系统退化之外。

这只是一个默认的大小。 如果你需要更多,你可以得到更多 – 通常告诉链接器分配额外的堆栈空间。

有大堆栈的缺点是,如果你创build了很multithreading,他们每个都需要一个堆栈。 如果所有堆栈都分配了多个MB,但是没有使用它,空间将被浪费。

你必须为你的程序find适当的平衡。


有些人,比如@BJovke,相信虚拟内存本质上是免费的。 确实,您不需要拥有支持所有虚拟内存的物理内存。 你必须至less能够给出虚拟内存的地址。

但是,在典型的32位PC上,虚拟内存的大小与物理内存的大小相同 – 因为我们只有32位用于任何虚拟地址。

因为进程中的所有线程共享相同的地址空间,所以它们必须在它们之间进行分割。 在操作系统发挥作用后,应用程序只剩下2-3 GB。 这个尺寸是物理虚拟内存的限制,因为没有更多的地址。

首先,堆栈是连续的,所以如果你分配12MB的数据,当你想要创build的时候,你必须删除12MB。 另外移动物体变得更加困难。 这是一个真实世界的例子,可以让事情变得更容易理解:

假设你在房间周围堆叠箱子。 哪个更容易pipe理:

  • 将任何重量的盒子堆叠在一起,但是当你需要在底部find某个东西时,你必须把你的整个堆放回去。 如果你想从一堆物品中取出物品并将物品交给别人,你必须把所有的箱子都取下来,然后把箱子移到另一个人的堆上(只有堆叠)
  • 你把所有的盒子(除了非常小的盒子)都放在一个特殊的区域,在这个特殊的区域里你不会把东西放在其他的东西上面,然后把你把它放在一张纸上(指针),然后把纸放在上面堆。 如果你需要把盒子交给其他人,你只需要把纸条从纸堆里拿出来,或者给他们一张纸的复印件,然后把它放在原来的地方。 (堆栈+堆)

这两个例子是粗略的概括,在类比中有一些公然错误的地方,但它已经足够接近,希望能帮助你看到两种情况的优点。

从近到远的顺序考虑堆栈。 寄存器靠近CPU(快速),堆栈稍微远一些(但仍然比较接近),堆远离(慢速访问)。

堆栈位于堆栈之上,但是由于它被连续使用,所以它可能永远不会离开CPUcaching,使其比平均堆访问更快。 这是保持合理大小的原因。 尽可能保持caching。 分配大堆栈对象(可能自动调整堆栈大小,因为你溢出)违背了这个原则。

所以这是一个很好的performance范式,而不仅仅是从旧时代的遗留下来。

许多你认为需要大堆的东西可以用其他方法来完成。

Sedgewick的“algorithm”有几个很好的例子,用recursionalgorithm(例如QuickSort)“去除”recursion,用recursionreplace迭代。 实际上,algorithm仍然是recursion的,并且仍然是堆栈,但是您在堆上分配了sorting堆栈,而不是使用运行时堆栈。

(我赞成第二版,用帕斯卡给出的algorithm,它可以用于八块钱。)

另一种看待它的方式是,如果你认为你需要一个大堆栈,你的代码是低效的。 有一个更好的方式,使用较less的堆栈。

我不认为有任何技术上的原因,但它会是一个奇怪的应用程序,只是在堆栈上创build一个巨大的超级对象。 堆栈对象缺乏灵活性,随着大小的增加,问题变得更加严重 – 如果不破坏它们,就不能返回,也不能将它们排队到其他线程。