堆栈什么时候真的溢出?

时间:2009-12-07 05:50:27

标签: stack

无限递归是唯一的情况还是因其他原因而发生? 堆栈大小是否与堆一样增长?

很抱歉,如果之前已经提出过这个问题,如果是这种情况,我们会很感激与他们的联系。

6 个答案:

答案 0 :(得分:10)

我不能代表所有平台,但事实上,我只是花了一些时间使用Windows .exe文件(我的意思是,实际上研究它们的二进制格式 - 我知道在某种意义上我们所有人这里使用可执行文件;))。我打赌大多数其他平台都有类似的功能,但我不是很熟悉它们。

部分文件格式本身包含两个与当前讨论相关的值:

typedef struct _IMAGE_OPTIONAL_HEADER {
    ...
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    ...
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

来自MSDN:

  

<强> SizeOfStackReserve

     

要为其保留的字节数   堆。只有指定的内存   SizeOfStackCommit成员是   在加载时承诺;剩下的就是   一次可用一页   直到达到这个储备金额。

     

<强> SizeOfStackCommit

     

要提交的字节数   叠加。

换句话说,链接器指定程序堆栈的最大大小。如果达到最大尺寸,则会溢出 - 无论您如何达到最大尺寸。你可以编写一个简单的程序,只需分配一个比最大堆栈大小更大的单个堆栈变量(比如一个数组),就可以在一行代码中完成。或者你可以通过无限(或有限但非常深)的递归来实现,或者只是通过分配太多的堆栈变量来实现。

在X86平台上,Microsoft链接器默认将此值设置为1MB(Itanium系统上为4MB)。对于现代系统来说,这看起来很小。但是,更现代的Windows版本对这些值的解释略有不同。它不是完全限制堆栈,而是限制堆栈将使用的物理内存。如果您的堆栈超出此范围,虚拟内存将会涉及,所以您应该仍然很好......假设您有足够的虚拟内存。

请记住, 可能会耗尽内存,即使在磁盘上有大量RAM和大量虚拟内存的现代系统上也是如此。您只需要分配大量数据。

所以,长话短说:是否可能在没有无限递归的情况下溢出堆栈?当然。可能吗?不是真的,除非你要分配真正庞大的物品。

答案 1 :(得分:5)

当堆栈指针被推出操作系统为堆栈分配的内存块时,堆栈溢出。一些操作系统会随着它的增长而调整堆栈大小(IIRC Linux会这样做),而在其他操作系统中,堆栈大小在进程或线程的开始时是固定的(IIRC Windows会这样做)。

堆栈溢出的可能原因:

  • 无限数量的堆栈帧(例如来自无界递归)
  • 尝试从堆栈中分配大块
  • 堆栈上分配的缓冲区的缓冲区溢出

也许还有其他原因,我无法想到我的头脑。

答案 2 :(得分:4)

此问题未指定哪个堆栈是“堆栈”。所以,这里有几个答案:

调用堆栈

只要堆栈上的调用次数超过其拥有的内存量,调用堆栈就会溢出。最常见的方法是无限递归,但很有可能让递归过多而不是无限递归。例如,天真地计算the Ackermann function会对任何计算机征税。

语言

基于堆栈的语言

某些语言(如Postscript和Forth)以及一些虚拟机(如Java虚拟机)都是基于堆栈的。在这些语言中,有可能使表达式如此复杂以至于它们溢出堆栈。

无上下文语言

无上下文语言通常使用堆栈实现。如果这些语言代码的字符串过于复杂,则可能会溢出堆栈。

答案 3 :(得分:2)

在笔记本电脑或台式机上,当从主线程运行时,没有无限(或非常深度嵌套)的递归溢出堆栈可能是不常见的......但是,堆栈溢出并不罕见:

       
  • 线程代码,其中线程已分配一个小的,固定大小的堆栈。
  •    
  • 信号处理代码,其中信号处理上下文具有小的固定大小的堆栈。
  •    
  • 代码在嵌入式设备上执行,内存通常很少。

例如,如果使用sigaction注册信号处理程序,如果信号处理程序执行任何复杂操作(即深度嵌套操作),则很容易在许多操作系统上获得堆栈溢出,因为信号处理程序通常分配一个小的,固定大小的堆栈。类似地,如果您使用pthread_create生成一个线程,但是使用pthread_attr_setstacksize指定了一个小的stacksize,那么很容易实现堆栈溢出。在非常受内存限制的设备(如无线传感器)上,避免堆栈溢出是一种艺术。

答案 4 :(得分:0)

我的日常工作涉及Lotus Notes中LotusScript的大量工作,它具有针对各种范围的固定堆栈限制。例如。过程/函数中的大多数变量必须适合32kB堆栈,除了类变量的内容存储在堆上。
如果固定大小的变量超过堆栈大小,代码将无法编译。
递归时可能会发生运行时堆栈溢出。这很容易在LotusScript中实现,因为它限制任何单个函数递归到32kB堆栈。因此,我几年前就放弃了使用递归QuickSort。

答案 5 :(得分:-1)

如果你的程序超过其分配的堆栈空间而没有任何无限递归,那么你做错了。

虽然如果你留下一些星号并尝试按值传递一些巨大的缓冲区,就会发生这种情况。

为堆栈分配的内存通常会在合理的边界内根据需要增长 - 我不确定各种系统的上限是什么。