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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10482974/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:07:53  来源:igfitidea点击:

why is stack memory size so limited?

c++memory-management

提问by undu

When you allocate memory on the heap, the only limit is free RAM (or virtual memory). It makes Gb of memory.

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

So why is stack size so limited (around 1 Mb) ? What technical reason prevents you to create really big objects on the stack ?

那么为什么堆栈大小如此有限(大约 1 Mb)?什么技术原因阻止您在堆栈上创建非常大的对象?

Update: My intent might not be clear, I do not wantto allocate huge objects on the stack and I do not needa bigger stack. This question is just pure curiosity.

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

采纳答案by user1202136

My intuition is the following. The stack is not as easy to manage as the heap. The stack need to be stored in continuous memory locations. This means that you cannot randomly allocate the stack as needed, but you need to at least reserve virtual addresses for that purpose. The larger the size of the reserved virtual address space, the fewer threads you can create.

我的直觉如下。堆栈不像堆那样易于管理。堆栈需要存储在连续的内存位置。这意味着您不能根据需要随机分配堆栈,但您至少需要为此目的保留虚拟地址。保留的虚拟地址空间越大,可以创建的线程就越少。

For example, a 32-bit application generally has a virtual address space of 2GB. This means that if the stack size is 2MB (as default in pthreads), then you can create a maximum of 1024 threads. This can be small for applications such as web servers. Increasing the stack size to, say, 100MB (i.e., you reserve 100MB, but do not necessarily allocated 100MB to the stack immediately), would limit the number of threads to about 20, which can be limiting even for simple GUI applications.

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

A interesting question is, why do we still have this limit on 64-bit platforms. I do not know the answer, but I assume that people are already used to some "stack best practices": be careful to allocate huge objects on the heap and, if needed, manually increase the stack size. Therefore, nobody found it useful to add "huge" stack support on 64-bit platforms.

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

回答by Andreas Grapentin

One aspect that nobody has mentioned yet:

还没有人提到的一个方面:

A limited stack size is an error detection and containment mechanism.

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

Generally, the main job of the stack in C and C++ is to keep track of the call stack and local variables, and if the stack grows out of bounds, it is almost always an error in the design and/or the behaviour of the application.

通常,C 和 C++ 中堆栈的主要工作是跟踪调用堆栈和局部变量,如果堆栈增长越界,则几乎总是设计和/或应用程序行为中的错误.

If the stack would be allowed to grow arbitrarily large, these errors (like infinite recursion) would be caught very late, only after the operating systems resources are exhausted. This is prevented by setting an arbitrary limit to the stack size. The actual size is not that important, apart from it being small enough to prevent system degradation.

如果允许堆栈任意增大,这些错误(如无限递归)将很晚才被捕获,只有在操作系统资源耗尽之后。这是通过对堆栈大小设置任意限制来防止的。除了足够小以防止系统降级之外,实际大小并不那么重要。

回答by Bo Persson

It is just a default size. If you need more, you can get more - most often by telling the linker to allocate extra stack space.

它只是一个默认大小。如果您需要更多,您可以获得更多——最常见的方法是告诉链接器分配额外的堆栈空间。

The downside to having large stacks is that if you create many threads, they will need one stack each. If all the stacks are allocating multi-MBs, but not using it, the space will be wasted.

拥有大堆栈的缺点是,如果您创建多个线程,则每个线程都需要一个堆栈。如果所有的堆栈都分配了多MB,但没有使用它,则会浪费空间。

You have to find the proper balance for your program.

您必须为您的程序找到适当的平衡点。



Some people, like @BJovke, believe that virtual memory is essentially free. It is true that you don't need to have physical memory backing all the virtual memory. You do have to be able to at least give out addresses to the virtual memory.

有些人,比如@BJovke,认为虚拟内存本质上是免费的。确实,您不需要物理内存来支持所有虚拟内存。您必须至少能够向虚拟内存分配地址。

However, on a typical 32-bit PC the size of the virtual memory is the same as the size of the physical memory - because we only have 32 bits for any address, virtual or not.

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

Because all threads in a process share the same address space, they have to divide it between them. And after the operating system has taken its part, there is "only" 2-3 GB left for an application. And that size is the limit for boththe physical andthe virtual memory, because there just aren't any more addresses.

由于进程中的所有线程共享相同的地址空间,因此它们必须在它们之间进行划分。在操作系统发挥作用之后,应用程序“仅”剩下 2-3 GB。这个大小对于极限两者的物理虚拟内存,因为那里只是没有任何其它地址。

回答by Scott Chamberlain

For one thing, the stack is continuous, so if you allocate 12MB, you must remove 12MB when you want to go below whatever you created. Also moving objects around becomes much harder. Here is a real world example that may make things easier to understand:

一方面,堆栈是连续的,因此如果您分配了 12MB,那么当您想要低于您创建的任何内容时,您必须删除 12MB。移动物体也变得更加困难。这是一个现实世界的例子,可以让事情更容易理解:

Say you are stacking boxes around a room. Which is easier to manage:

假设您正在房间周围堆放箱子。哪个更容易管理:

  • stacking boxes of any weight on top of each other, but when you need to get something on the bottom you have to undo your entire pile. If you want to take a item out of the pile and give it to someone else you must take off all of the boxes and move the box to the other person's pile (Stack only)
  • You put all of your boxes (except for really small boxes) over in a special area where you do not stack stuff on top of other stuff and write down where you put it on a piece of paper (a pointer) and put the paper on the pile. If you need to give the box to someone else you just hand them the slip of paper from your pile, or just give them a photocopy of the paper and leave the original where it was in your pile. (Stack + heap)
  • 将任何重量的箱子堆叠在一起,但是当您需要在底部放一些东西时,您必须撤消整堆。如果您想从堆中取出一件物品并将其交给其他人,您必须取下所有箱子并将箱子移到其他人的堆中(仅限堆叠)
  • 你把你所有的盒子(除了非常小的盒子)放在一个特殊的区域,你不要把东西堆放在其他东西上面,然后在一张纸(一个指针)上写下你把它放在什么地方,然后把纸放在上面桩。如果您需要将盒子交给其他人,您只需将纸条从您的纸堆中递给他们,或者给他们一份纸的复印件并将原件留在您的纸堆中。(堆栈+堆)

Those two examples are gross generalizations and there are some points that are blatantly wrong in the analogy but it is close enough that it hopefully will help you see the advantages in both cases.

这两个例子是粗略的概括,在类比中有一些明显错误的点,但它足够接近,希望能帮助您看到两种情况下的优势。

回答by Ruud van Gaal

Think of the stack in the order of near to far. Registers are close to the CPU (fast), the stack is a bit further (but still relatively close) and the heap is far away (slow access).

按照从近到远的顺序考虑堆栈。寄存器靠近CPU(快),栈有点远(但仍然相对接近),堆远离(慢访问)。

The stack lives on the heap ofcourse, but still, since it's being used continuously, it probably never leaves the CPU cache(s), making it faster than just the average heap access. This is a reason to keep the stack reasonably sized; to keep it cached as much as possible. Allocating big stack objects (possibly automatically resizing the stack as you get overflows) goes against this principle.

堆栈当然存在于堆上,但是,由于它被连续使用,它可能永远不会离开 CPU 缓存,使其比普通堆访问更快。这是保持堆栈合理大小的一个原因;尽可能地保持缓存。分配大堆栈对象(可能会在溢出时自动调整堆栈大小)违背了这一原则。

So it's a good paradigm for performance, not just a left-over from old times.

因此,它是性能的一个很好的范例,而不仅仅是旧时代的遗留物。

回答by Mike Crawford

Many of the things you think you need a big stack for, can be done some other way.

许多您认为需要大量堆栈的事情可以通过其他方式完成。

Sedgewick's "Algorithms" has a couple good examples of "removing" recursion from recursive algorithms such as QuickSort, by replacing the recursion with iteration. In reality, the algorithm is still recursive, and there is still as stack, but you allocate the sorting stack on the heap, rather than using the runtime stack.

Sedgewick 的“算法”有几个很好的例子,通过用迭代替换递归,从递归算法(如 QuickSort)中“删除”递归。实际上,该算法仍然是递归的,并且仍然存在堆栈,但是您在堆上分配排序堆栈,而不是使用运行时堆栈。

(I favor the second edition, with algorithms given in Pascal. It can be had used for eight bucks.)

(我更喜欢第二版,算法在 Pascal 中给出。它可以花八块钱使用。)

Another way to look at it, is if you think you need a big stack, your code is inefficient. There is a better way that uses less stack.

另一种看待它的方式是,如果您认为您需要一个大堆栈,那么您的代码效率很低。有一种使用更少堆栈的更好方法。

回答by Martin James

I don't think there is any technical reason, but it would be a strange app that just created just one huge super-object on the stack. Stack objects lack flexibility that becomes more problematic with increasing size - you cannot return without destroying them and you cannot queue them to other threads.

我不认为有任何技术原因,但这将是一个奇怪的应用程序,它只是在堆栈上创建了一个巨大的超级对象。堆栈对象缺乏灵活性,随着大小的增加而变得更加成问题 - 您无法在不销毁它们的情况下返回,并且无法将它们排入其他线程。