1

所以我写了一个玩具 C 程序,它会故意导致堆栈溢出,只是为了玩弄我系统的限制:

#include <stdio.h>

int kefladhen(int i) {
    int j = i + 1;
    printf("j is %d\n",j);
    kefladhen(j);
}

int main() {
    printf("Hello!:D\n");
    kefladhen(0);
}

我惊讶地发现在分段错误之前打印的最后一行是“j is 174651”。当然,每次运行它的确切数字都会有所不同,但总的来说,我很惊讶 174 万奇数的堆栈帧足以耗尽我的 4GB linux 笔记本电脑上的进程的内存。我认为 printf 可能会产生一些开销,但是 printf 在我递归调用 kefladhen() 之前返回,因此堆栈指针应该回到以前的位置。我每次调用只存储一个 int,所以每个堆栈帧总共应该只有 8 个字节,对吧?因此,其中 17.4 万个只是实际使用的内存的大约 1 兆字节半,这对我来说似乎太低了。我在这里有什么误解?

4

3 回答 3

8

...但总的来说,我很惊讶 174 万奇数堆栈帧足以耗尽我 4GB linux 笔记本电脑上进程的内存...

请注意,堆栈不是通用内存池。堆栈是为提供堆栈而预先分配的块。机器上的 4GB 内存可能只有 1MB。我的猜测是您的堆栈大小约为 1.3MB;这对于 174,651 个 8 字节帧(返回地址 4 字节, . 字节 4 字节int)已经足够了。

于 2012-04-30T10:54:56.467 回答
2

我认为这里的关键误解是堆栈本身不会动态增长。它被静态设置为一个相对较小的数字,但您可以在运行时更改它(这里是一个答案的链接,解释了它是如何使用setrlimitcall完成的)。

于 2012-04-30T10:58:09.027 回答
1

其他人已经讨论过堆栈的大小和分配。“每次运行它的确切数字都会略有不同”的原因与多线程系统上的缓存性能有关。

通常,您可以期望为堆栈预分配的内存是页面对齐的。但是,起始堆栈指针会因线程到线程/进程到进程/任务到任务而异。这是为了帮助避免缓存行刷新、失效和加载。如果所有任务/线程/进程都具有相同的堆栈指针虚拟地址,则您会期望每当发生上下文切换时都会发生更多缓存冲突。为了减少这种可能性,许多操作系统将起始堆栈指针放在起始堆栈页面的某个位置,但不一定位于最顶部或相同位置。因此,当发生上下文切换并发生后续堆栈访问时,...

  1. 变量已经在缓存中的机会更大
  2. 不会发生缓存冲突的更好机会

希望这可以帮助。

于 2012-04-30T13:56:47.767 回答