如果没有足够的内存来分配当前函数的堆栈(局部)变量,操作系统是否会在阈值之前向用户发送警告,然后应用程序实际上会崩溃?
发布于 2012-03-01 22:32:52
好吧..。从语义上讲,没有堆栈。
从语言的角度来看,自动存储只是正常工作,动态存储可能会以特定的方式失败(malloc返回NULL,new抛出std::bad_alloc)。
当然,实现通常会带来一个堆栈来实现自动存储,而且这个堆栈的大小是有限的。然而,这只是一个实现细节,并不需要如此。
例如,gcc -fsplit-stack允许您拥有一个根据需要增长的分段堆栈。这项技术对于C或C++ AFAIK来说是相当新的,但是像Haskell这样具有延续(以及数千或数百万)的语言也有这种内置的,Go也有关于它的观点。
尽管如此,在某些时候,如果你继续敲打,记忆将会耗尽。这实际上是未定义的行为,因为标准根本没有尝试处理这一问题。在这种情况下,通常情况下,操作系统将向程序发送一个信号,该信号将关闭,堆栈将不会展开。
发布于 2012-03-01 22:21:16
是的,你会得到一个堆栈溢出运行时错误。
附注:有一个流行的网站以这个错误命名!
发布于 2012-03-01 22:24:31
堆栈分配可能会失败,您对此无能为力。
在现代操作系统上,将为堆栈分配大量内存(目前在Linux上似乎为128k左右),并为堆栈增长保留一定范围的虚拟地址(通常要大得多,例如Linux上的8M,通常是可配置的)。如果超过了提交的部分,提交更多的内存可能会因为内存不足而失败,并且您的程序将使用SIGSEGV崩溃。如果你超过了保留的地址范围,你的程序肯定会失败,如果它覆盖了堆栈地址范围下的其他数据,可能会是灾难性的。
解决方案是不要对堆栈做疯狂的事情。即使是Linux上的初始提交空间(128k)也会比您应该使用的堆栈空间更多。不要使用调用递归,除非您对调用级别的数量有一个对数限制,不要使用巨大的自动数组或结构(包括可能由用户提供的VLA维度产生的数组或结构),这样就没问题了。
请注意,没有可移植和未来安全的方法来测量当前堆栈的使用情况和剩余的可用性,因此您只需对其保持安全即可。
编辑:关于堆栈分配,至少在现实系统中,(没有拆分堆栈)的一个保证是,你已经验证过的堆栈空间不会神奇地消失。例如,如果您成功地从b()从a()从main()调用了c(),并且它们没有使用任何大小可能不同的VLA,则在您的程序的同一实例中重复相同的调用模式不会失败。您还可以找到一些工具来对一些程序(没有使用函数指针和/或递归的程序)执行静态分析,这些工具将确定您的程序曾经消耗的最大堆栈空间量,在此之后,您可以在程序启动时进行设置,以验证您可以在继续之前成功地使用那么多空间。
https://stackoverflow.com/questions/9517724
复制相似问题