需要超过 16GB 的连续内存时,内存分配失败

1 c++ linux malloc dynamic-memory-allocation ubuntu-16.04

我的工作站有 128GB 内存。我无法分配占用(连续)内存超过 ~16GB 的数组。但是我可以分配多个数组,每个数组大约需要 15GB。

示例代码:

#include <stdlib.h>
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
int MM = 1000000;
int NN = 2200; // 2000 is okay, used ~16GB memory; 2200 produces Segmentation fault

double* testMem1d;
testMem1d  = (double*) malloc(MM*NN*sizeof(double));

double* testMem1d1; // NN=2000, allocate another array (or two) at the same time is okay
testMem1d1 = (double*) malloc(MM*NN*sizeof(double));

cout << "testMem1d allocated" << endl;
cin.get(); // here is okay, only malloc but not accessing the array element

cout << "testMem1d[MM*NN-1]=" << testMem1d[MM*NN-1]<< endl;
cout << "testMem1d1[MM*NN-1]=" << testMem1d1[MM*NN-1]<< endl;

// keep running and check the physical memory footprint
for (int tt=0;tt<1000;tt++)
{
    for (int ii=0; ii<MM*NN; ii++)
    {
        testMem1d[ii]=ii;
        testMem1d1[ii]=ii;
    }
    cout << "MM=" << MM << ", NN=" << NN << ", testMem1d[MM*NN-1]=" << testMem1d[MM*NN-1]<< endl;
}
}
Run Code Online (Sandbox Code Playgroud)

如果不是基本问题,请忽略我在 C++ 中使用 malloc()。(是吗?)出于其他原因,我需要/想要使用 malloc()。

一些观察:(1) 分配多个数组,每个数组小于 15GB,没问题 (2) 只做 malloc() 没问题。访问数组元素时出现“分段错误”。

我认为可能有一些系统设置限制了内存分配。从“ulimit -a”开始,一切似乎都很好。由于程序可以访问 64 位虚拟地址空间,我找不到任何仅限制连续内存分配的原因。

操作系统:Ubuntu 16.04。我用 mcmodel=large 尝试了 g++ 和 icc。这似乎无关紧要。

uname -a
Linux 4.4.0-143-generic #169-Ubuntu SMP Thu Feb 7 07:56:38 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

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) 515031
max locked memory       (kbytes, -l) unlimited
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 515031
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
Run Code Online (Sandbox Code Playgroud)

编辑:

(1) mallc() 实际上返回NULL [给mcleod_ideafix]

(2) [对zwol]

free -m
              total        used        free      shared  buff/cache   available
Mem:         128809       18950      107840        1129        2018      107910
Swap:           974         939          35
Run Code Online (Sandbox Code Playgroud)

Kam*_*Cuk 6

乘法MM*NN*sizeof(double)是左结合的,所以它发生为(MM * NN) * sizeof(double)。在具有 32 位的平台int上,方程MM * NNis 等于2200000000which 不能用 32 位表示int并且溢出(并且发生未定义的行为)并环绕导致-2094967296. 然后这个值被提升为普通类型sizeof(double), to size_t。这是有符号类型到无符号类型的转换,其中有符号值不能用无符号类型表示,因此转换是实现定义的。在带有 64 位size_t符号扩展的二进制补码中发生,它应该导致18446744071614584320. 然后这个值乘以sizeof(double)我假设等于8,它溢出多次(这是安全的,size_t无符号)并以18446744056949813248字节为单位。你的机器没有那么多内存,所以malloc返回NULL。

这就是为什么最好将sizeof第一个操作数放入malloc调用中的原因:

malloc(sizeof(double) * MM * NN);
Run Code Online (Sandbox Code Playgroud)

在这种情况下,操作数将size_t在乘法之前被提升。

但这还不够,因为 intestMem1d[MM*NN-1]和 inii<MM*NN溢出仍然发生。所以,你应该改变的类型MM,并NN用足够的位来保存结果的类型。

size_t MM = 1000000;
size_t NN = 2200;
Run Code Online (Sandbox Code Playgroud)

或者在每次可能溢出的乘法之前将值转换为正确的类型。

请注意,语句cout << "testMem1d[MM*NN-1]=" << testMem1d[MM*NN-1]<< endl; cout << "testMem1d1[MM*NN-1]=" << testMem1d1[MM*NN-1]<< endl;正在读取未初始化的内存。

更喜欢new在 C++ 中使用。