【发布时间】:2009-12-07 05:50:27
【问题描述】:
无限递归是唯一的情况还是会因为其他原因而发生? 栈大小不是和堆一样按需增长吗?
很抱歉,如果以前有人问过这个问题,如果是这样的话,将不胜感激他们的链接。
【问题讨论】:
标签: stack
无限递归是唯一的情况还是会因为其他原因而发生? 栈大小不是和堆一样按需增长吗?
很抱歉,如果以前有人问过这个问题,如果是这样的话,将不胜感激他们的链接。
【问题讨论】:
标签: stack
我不能代表所有平台,但碰巧的是,我刚刚花了一些时间处理 Windows .exe 文件(我的意思是,实际上研究它们的二进制格式 - 从某种意义上说,我知道我们所有人这里使用可执行文件;))。我敢打赌,大多数其他平台都具有类似的功能,但我对它们并不熟悉。
部分文件格式本身包含两个与当前讨论相关的值:
typedef struct _IMAGE_OPTIONAL_HEADER {
...
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
...
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
来自 MSDN:
SizeOfStackReserve
要保留的字节数 堆。只有指定的内存 SizeOfStackCommit 成员是 在加载时提交;剩下的就是 一次提供一页 直到达到这个保留大小。
SizeOfStackCommit
提交的字节数 堆栈。
换句话说,链接器指定程序堆栈的最大大小。如果你达到最大尺寸,你就会溢出——不管你如何达到最大尺寸。您可以编写一个简单的程序来在一行代码中执行此操作,只需分配一个大于最大堆栈大小的堆栈变量(例如,一个数组)。或者您可以通过无限(或有限,但非常深)递归来实现,或者只是分配太多堆栈变量。
Microsoft 链接器在 X86 平台上默认将此值设置为 1MB(在 Itanium 系统上为 4MB)。对于现代系统而言,这从表面上看似乎很小。但是,更现代的 Windows 版本对这些值的解释略有不同。它不是完全限制堆栈,而是限制堆栈将使用的物理内存。如果您的堆栈超出此范围,则会涉及虚拟内存,因此您应该仍然很好......假设您有足够的虚拟内存。
请记住,即使在具有大量 RAM 和大量磁盘虚拟内存的现代系统上,也可能会耗尽内存。你只需要分配大量的数据。
所以,长话短说:有可能在没有无限递归的情况下溢出堆栈吗?确实。有可能吗?不是真的,除非你分配了非常大的对象。
【讨论】:
当堆栈指针被推出操作系统为堆栈分配的内存块时,堆栈溢出。一些操作系统会随着堆栈的增长而调整大小(IIRC Linux 会这样做),而在其他操作系统中,堆栈大小会在进程或线程开始时固定(IIRC Windows 会这样做)。
堆栈溢出的可能原因:
可能还有其他我无法想到的原因。
【讨论】:
这个问题没有指定哪个堆栈是“那个”堆栈。所以,这里有几个答案:
每当堆栈上的调用次数超出其拥有的内存量时,调用堆栈就会溢出。最常见的方法是无限递归,但很有可能递归过度但不是无限的。例如,天真地计算the Ackermann function 会对任何计算机征税。
一些语言,如 Postscript 和 Forth,以及一些虚拟机,如 Java 虚拟机,是基于堆栈的。在这些语言中,可能会使表达式变得如此复杂以至于溢出堆栈。
上下文无关语言通常使用堆栈来实现。如果这些语言的代码字符串过于复杂,则可能会溢出堆栈。
【讨论】:
在笔记本电脑或台式机上,从主线程运行时,如果没有无限(或非常深的嵌套)递归,堆栈溢出可能是不寻常的......但是,堆栈溢出并不少见:
例如,如果您使用sigaction 注册信号处理程序,如果信号处理程序执行任何复杂的操作(即深度嵌套操作),在许多操作系统上很容易发生堆栈溢出,因为信号处理程序通常分配一个小的、固定大小的堆栈。同样,如果您使用pthread_create 生成一个线程,但您使用pthread_attr_setstacksize 指定一个小的堆栈大小,那么很容易实现堆栈溢出。在无线传感器等内存非常有限的设备上,避免堆栈溢出是一门艺术。
【讨论】:
我的日常工作涉及大量使用 Lotus Notes 中的 LotusScript,它对各种范围都有固定的堆栈限制。例如。过程/函数中的大多数变量必须适合 32kB 堆栈,除了类变量的内容存储在堆上。
如果固定大小的变量超过堆栈大小,代码将无法编译。
运行时堆栈溢出可能发生在递归中。这在 LotusScript 中很容易实现,因为它将任何单个函数的递归限制为 32kB 堆栈。几年前,我因此放弃了使用递归快速排序。
【讨论】:
如果您的程序超出了分配的堆栈空间而没有进行任何无限递归,那么您做错了什么。
虽然如果你去掉一些星号并尝试按值传递一些巨大的缓冲区,它可能会发生。
为堆栈分配的内存通常会在合理的范围内按需增长 - 我不确定各种系统的上限是多少。
【讨论】: