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

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

我想知道在分配失败之前我可以分配多少内存。

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

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

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] = '[=10=]';

                // 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 个字节。我 1980 年代的 ZX81 可以应付。

MacOS X 和几乎每个现代操作系统一样,使用 "delayed allocation" 作为内存。当您调用 new 时,OS 实际上并未分配任何内存。它只是简单地记录下您的程序需要一定数量的内存,并且您想要的内存区域从某个地址开始。内存仅在您的程序尝试使用它时才实际分配。

此外,内存是以 "pages" 为单位分配的。我相信 MacOS X 使用 4kb 页面,所以当你的程序写入缓冲区的末尾时,OS 在那里给你 4096 个字节,同时将缓冲区的其余部分保留为简单的 "your program wants this memory" 注意.

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

Virtual memory 是分配比物理 RAM+swap space.

更多地址 space 的关键

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

写入任何这些页面都会触发软页面错误,以便内核处理写时复制。内核分配物理内存的零页面,并重新连接该虚拟页面以由物理页面支持。在页面错误 return 上,存储重新执行并这次成功。

因此在分配 64TiB 并在其末尾存储 5 个字节后,您已经使用了额外的一页物理内存。 (并在 malloc 的簿记数据中添加了一个条目,但它可能已经分配并在脏页中。在关于多个微小分配的类似问题中,malloc's bookkeeping data was what eventually used up all the space)。

如果实际上弄脏的页面多于系统的 RAM + 交换空间,内核就会有问题,因为 malloc 到 return NULL 已经太迟了。这称为 "overcommit",有些 OS 会默认启用它,而另一些则不会。在 Linux 中,它是可配置的。


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

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