当您在堆栈中请求的 space 多于可用资源时会发生什么?

What happens when you request more space in the stack than what is available?

本题基于C/C++内存分配。

我仔细阅读了堆栈和堆之间的区别,但有一件事让我感到困惑。应该在堆中为大对象分配内存,但也可以在堆栈中将其作为局部变量分配。

从这个线程(C/C++ maximum stack size of program)我了解到堆栈是有限的并且限制相对较低(最大 7.4MB)。

我用以下程序测试了这个限制:

#include <vector> 
int main() {
std::vector<double> test;

for (int i = 0; i < 5000000; i++){
    test.push_back(i);
}

return 0;
}

分配的总内存为 8 字节 * (5.000.000) = 40MByte。 这似乎不会引起任何类型的错误。我在这个资源 (https://software.intel.com/en-us/articles/determining-root-cause-of-sigsegv-or-sigbus-errors) 上读到,堆栈溢出可能会引发分段错误或总线错误。

所以我想,问题是:当您 "allocate" 堆栈中的内存超出您的能力时会发生什么?

溢出堆栈的行为取决于平台。官方术语可能是"undefined behavior",意思是任何事情都有可能发生。

平台不需要实现堆栈,尽管它是一种常见的技术。

一些平台为堆栈和堆都预留了内存,并且让它们"grow" 朝向彼此(画图)。因此,如果堆栈溢出,它会开始在堆上写入,反之亦然。

部分平台可能设置了硬件围栏,当处理器访问超出范围的内存时,会产生硬件异常。 OS 将处理该异常。

另一个例子是您的程序开始写入某种硬件设备,例如 USB 控制器或磁盘驱动器控制器。

总而言之,Stack Overflow 的行为取决于平台,包括恢复(如果有)。

std::vector 在堆上分配内存,而不是堆栈。如果你想测试粘性分配,最简单的方法是使用如下程序:

#include <cstdio>
int main(void) {
    char temp[1024*1024*40] = {};
    printf("%s\n",temp);
    return 0;
}

(N.B。打印是为了防止缓冲区被优化掉。)
这会在堆栈上分配 40 MiB,并产生堆栈溢出(参见 live)。

另一种方法是递归调用函数。例如:

unsigned factorial_times_2(unsigned n) {
    unsigned result;

    if (n<2u) result=1u;
    result = n * (factorial_times_2(n-1u)/2u);

    return result * 2u;
}
int main(void) {
    return factorial_times_2(~0u)/2u;
}

这是对经典递归阶乘函数的简单修改(修改,因为现代编译器将使简单阶乘尾递归)。在运行时,它将尝试生成大约 40 亿个堆栈帧。产生堆栈溢出(参见 live)。


如您所料,堆栈溢出意味着您超出了分配给堆栈的内存。由于堆栈通常分配有自己的页面,因此离开堆栈就是离开有效的映射内存地址。

因此,堆栈溢出通常会导致段错误(如上例中发生的那样)。在 Windows 上,它被称为访问冲突。如果你不那么幸运,它会破坏你的程序数据,你以后才会发现。