为什么声明足够大的 2D 数组会导致 Linux 而不是 macOS 上的段错误?

Why does declaring a 2D array of sufficient size cause a segfault on Linux but not macOS?

问题

我试图在 C/C++ 中声明一个大型二维数组(a.k.a. 矩阵),但它仅在 Linux 上因段错误而崩溃。 Linux 系统安装的 RAM 比 macOS 笔记本电脑多得多,但它只在 Linux 系统上崩溃。

我的问题是:为什么只在 Linux 而不是 macOS 上崩溃?

这是一个重现问题的小程序:

// C++ program to segfault on linux
#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
    cout << "Let's crash for no raisin! " << endl;

    cout << "Int size: " << sizeof(int) << endl;
    for (int n=1012; n < 2000; n++) {
      cout << "Declaring Matrix2D of size: " << n << "x" << n << " = " << n*n << endl;
      cout << "Bytes: " << n*n*sizeof(int) << endl;

      // segfault on my machine at 1448x1448 = 8386816 bytes
      int Matrix2D[n][n];
      // these two lines can be commented out and the program still reaches segfault
      // int* pM2D = (int*)malloc(n*n*sizeof(int));
      // free(pM2D);
    }
    return 0;
}

编译为:g++ -Wall -g -o segfault segfault.cpp

输出

Linux

Linux 系统安装了 64 GiB 内存!

$ ./segfault ; free --bytes
Let's crash for no raisin! 
Int size: 4

[...SNIP...]

Declaring Matrix2D of size: 1446x1446 = 2090916
Bytes: 8363664
Declaring Matrix2D of size: 1447x1447 = 2093809
Bytes: 8375236
Declaring Matrix2D of size: 1448x1448 = 2096704
Bytes: 8386816
Segmentation fault (core dumped)


              total        used        free      shared  buff/cache   available
Mem:    67400994816 11200716800  4125982720   412532736 52074295296 55054041088
Swap:    1023406080   824442880   198963200

$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 256763
max locked memory       (kbytes, -l) 65536
max memory size         (kbytes, -m) unlimited
open files                      (-n) 65535
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 256763
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
macOS

macOS系统只安装了16GB内存!

$ ./segfault ; sysctl -a | grep mem ;
Let's crash for no raisin! 
Int size: 4

[...SNIP...]

Declaring Matrix2D of size: 1997x1997 = 3988009
Bytes: 15952036
Declaring Matrix2D of size: 1998x1998 = 3992004
Bytes: 15968016
Declaring Matrix2D of size: 1999x1999 = 3996001
Bytes: 15984004


kern.dtrace.buffer_memory_maxsize: 5726623061
kern.dtrace.buffer_memory_inuse: 0
kern.memorystatus_sysprocs_idle_delay_time: 10
kern.memorystatus_apps_idle_delay_time: 10
kern.memorystatus_purge_on_warning: 2
kern.memorystatus_purge_on_urgent: 5
kern.memorystatus_purge_on_critical: 8
vm.memory_pressure: 0
hw.memsize: 17179869184
machdep.memmap.Conventional: 17077571584
machdep.memmap.RuntimeServices: 524288
machdep.memmap.ACPIReclaim: 188416
machdep.memmap.ACPINVS: 294912
machdep.memmap.PalCode: 0
machdep.memmap.Reserved: 84250624
machdep.memmap.Unusable: 0
machdep.memmap.Other: 0

$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
file size               (blocks, -f) unlimited
max locked memory       (kbytes, -l) unlimited
max memory size         (kbytes, -m) unlimited
open files                      (-n) 256
pipe size            (512 bytes, -p) 1
stack size              (kbytes, -s) 65532
cpu time               (seconds, -t) unlimited
max user processes              (-u) 2784
virtual memory          (kbytes, -v) unlimited

虽然 ISO C++ 不支持 variable-length arrays,但您似乎正在使用支持它们作为扩展的编译器。

在行

int Matrix2D[n][n];

n 的值最大为 2000。这意味着二维数组可以有 2000*2000 个元素,等于 400 万。每个元素的大小为 sizeof(int),即 linux 上的 4 字节。这意味着您在堆栈上总共分配了 16 兆字节。这超出了堆栈的限制,导致 stack overflow.

它在 MacOS 上没有崩溃的原因可能是堆栈配置了更高的最大限制,或者可能是你的程序没有崩溃,因为可变长度数组的实现方式不同,所以程序没有接触二维阵列,或者它可能接触了二维阵列,但只是以不会导致崩溃的方式。这些是编译器的实现细节。

与计算机实际安装的内存量无关。重要的是操作系统中配置的最大堆栈限制。

如果您想使用比堆栈允许的更多的内存,您应该改用堆。在这种情况下,您应该分配内存而不是 std::make_unique, operator new or std::malloc. You can also use most STL containers such as std::vector, which will automatically store its contents on the heap, even if you create the actual container on the stack. However, beware that some STL containers will not, such as std::array.