我可以在 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)。一个 x86 页面是 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 的未来硬件上中断。
我想知道在分配失败之前我可以分配多少内存。
这个简单的 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)。一个 x86 页面是 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 的未来硬件上中断。