我可以在 MacBook Pro 上分配的最大内存量是多少?

What is the largest amount of memory I can allocate on my MacBook Pro?

本文关键字:内存 大内 多少 MacBook Pro 分配 我可以      更新时间:2023-10-16

我试图弄清楚在分配失败之前我可以分配多少内存。

这个简单的C++代码分配一个缓冲区(大小为 1024 字节),分配给缓冲区的最后五个字符,报告,然后删除缓冲区。然后,它将缓冲区的大小加倍并重复,直到失败。

除非我错过了什么,否则代码能够在我的MacBook Pro上失败之前分配多达65TB的内存。这可能吗?它怎么能分配比我在机器上多得多的内存?我一定错过了一些简单的东西。

int main(int argc, char *argv[])
{
long long size=1024;
long cnt=0;
while (true)
{
char *buffer = new char[size];
// Assume the alloc succeeded. We are looking for the failure after all.
// Try to write to the allocated memory, may fail
buffer[size-5] = 'T';
buffer[size-4] = 'e';
buffer[size-3] = 's';
buffer[size-2] = 't';
buffer[size-1] = '';
// report
if (cnt<10)
cout << "size[" << cnt << "]: " << (size/1024.) << "Kb ";
else if (cnt<20)
cout << "size[" << cnt << "]: " << (size/1024./1024.) << "Mb ";
else
cout << "size[" << cnt << "]: " << (size/1024./1024./1024.) << "Gi ";
cout << "addr: 0x" << (long)buffer << " ";
cout << "str: " << &buffer[size-5] << "n";
// cleanup
delete [] buffer;
// double size and continue
size *= 2;
cnt++;
}
return 0;
}

当您要求内存时,操作系统保留在您实际使用它之前不实际为您提供该内存的权利。

这就是这里发生的事情:你只使用了 5 个字节。我 81 年代的 ZX1980 可以处理这个问题。

MacOS X与几乎所有现代操作系统一样,对内存使用"延迟分配"。 当您调用new时,操作系统实际上不会分配任何内存。 它只是注意到您的程序需要一定量的内存,并且您想要的内存区域从某个地址开始。 内存仅在程序尝试使用它时才实际分配。

此外,内存以称为"页"的单位分配。 我相信 MacOS X 使用 4kb 页面,所以当你的程序写入缓冲区的末尾时,操作系统会给你 4096 字节,同时保留缓冲区的其余部分作为简单的"你的程序想要这个内存"的注释。

至于为什么达到64TB的限制,这是因为当前的x86-64处理器使用48位寻址。 这将提供 256 TB 的地址空间,该空间在操作系统和程序之间平均分配。 将 64 TB 分配加倍将完全适合程序 128 TB 的一半地址空间,只是程序已经占用了一小部分地址空间。

虚拟内存是分配比物理 RAM+交换空间更多的地址空间的关键。

malloc 使用mmap(MAP_ANONYMOUS)系统调用从操作系统获取页面。 (假设OS X像Linux一样工作,因为它们都是POSIX操作系统)。 这些页面都是写入时复制映射到单个物理零页的。 即它们都读取为零,只有 TLB 未命中(没有页面错误,也没有分配物理 RAM)。 x86 页面是 4kiB。 (我没有提到大页面,因为它们在这里无关紧要)。

写入其中任何页面都会触发软页面错误,内核无法处理写入时复制。 内核分配物理内存的清零页,并重新连接该虚拟页以由物理页支持。 从页面错误返回时,存储将重新执行并成功。

因此,在分配 64TiB 并在其末尾存储 5 个字节后,您已经使用了额外的一页物理内存。 (并在malloc的簿记数据中添加了一个条目,但该条目可能已经分配并且位于一个肮脏的页面中。 在一个关于多个微小分配的类似问题中,malloc的簿记数据最终耗尽了所有空间)。

如果您实际上弄脏的页面多于系统的 RAM + 交换,则内核将出现问题,因为malloc返回 NULL 为时已晚。 这称为"过度提交",某些操作系统默认启用它,而其他操作系统则不启用。 在Linux中,它是可配置的。


正如 Mark 所解释的那样,您在 64TiB 时失去了动力,因为当前的 x86-64 实现仅支持 48 位虚拟地址。 上面的 16 位必须是位 47 的副本。 (即,仅当 64 位值是低 48 位的符号扩展时,地址才是规范的)。

此要求阻止程序对高位执行任何"聪明"操作,然后在支持更大虚拟地址空间的未来硬件上中断。