【问题标题】:What is the largest amount of memory I can allocate on my MacBook Pro? [duplicate]我可以在 MacBook Pro 上分配的最大内存量是多少? [复制]
【发布时间】:2017-05-14 23:53:45
【问题描述】:

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

这个简单的 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] = '\0';

                // 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;
}

【问题讨论】:

  • 是在内存“随机存取内存”还是在“硬盘”上分配变量?
  • 顺便说一句:如果你真的想确定你可以分配的实际最大值,你应该在第一次失败后使用一个额外的循环,将添加到分配请求的数量减少两倍每次直到你增加你想要检查的最小值。例如:512G​​iB请求OK,1024GiB请求失败,则请求(512GiB+256GiB)=768GiB(如果OK则(768GiB+128GiB)=896GiB,如果失败则尝试(512GiB+128GiB)=640GiB)等。
  • macbook pro 与否在这里无关紧要,你不需要把它放在标题中。 Allocating more memory than there exists using malloc, maximum memory which malloc can allocate
  • 您可能相信内存就是 RAM 的神话。内存不是 RAM。内存是对存储和检索数据能力的抽象,这种抽象可以用许多不同种类的硬件来实现。 RAM 只是快速方便的解决方案。您最好将内存视为磁盘空间,将 RAM 视为缓存,以便更快地访问磁盘,因为现在就是这样。

标签: c++ memory allocation


【解决方案1】:

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

这就是这里发生的事情:您只使用了 5 个字节。我 1980 年代的 ZX81 可以应付。

【讨论】:

  • 那又怎样,操作系统比你想象的要聪明。你不认为 macbook pro 只是好看吗?
  • 哦。因此,如果我写入每个缓冲区中的每个字符,它会更早地失败吗?你说的是这个吗?
  • 是的。一个精心设计的操作系统会给你内存块;通常称为页面
  • google://memory+overcommitment
  • 你的ZX81没有虚拟内存吧?这是分配比物理+交换空间更多的关键,但只需要一页物理 RAM 来处理你弄脏的单页。其他页面保持只读映射到物理零页面。
【解决方案2】:

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

此外,内存是以称为“页”的单位分配的。我相信 MacOS X 使用 4kb 页面,因此当您的程序写入缓冲区的末尾时,操作系统会在此处为您提供 4096 个字节,同时保留缓冲区的其余部分作为“您的程序需要此内存”的注释。

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

【讨论】:

  • 他们使用额外的 16 位地址做什么?
  • @tbodt - 他们没有。基本上,它理论上是一个 64 位地址空间,但由于布局原因,高 16 位是未连线的,因为无论如何芯片在物理上都无法支持那么多内存。这使得硅设计更容易,因为你没有这些庞大的总线部分,但实际上并没有做任何事情。
  • 请注意,Windows 也会延迟分配,但它不会过度使用,因此在 Windows 上,您分配的内存不应超过总 RAM + 交换空间- 内存已在使用中。
  • 我不认为任何操作系统默认使用大页面(x86-64 为 2MiB 或 1GiB)(Linux 的透明大页面除外),所以是的,正常的 4kiB x86 页面是一个安全的猜测。我发布了一个答案,其中包含有关写时复制延迟分配的工作原理的更多详细信息。
【解决方案3】:

Virtual memory 是分配比物理 RAM+交换空间更多的地址空间的关键。

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

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

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

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


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

此要求阻止程序使用高位做任何“聪明”的事情,然后破坏未来支持更大虚拟地址空间的硬件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-02
    • 2011-09-26
    • 2019-10-07
    • 1970-01-01
    • 2018-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多