在 64 位系统上创建一个非常大的数组有什么缺点?

What would be the disadvantage of creating an array of really big size on 64 bit systems?

像 Linux 这样的操作系统基于写时复制的原则工作,所以即使您分配一个 100 GB 的数组,但最多只使用 10 GB,您也只会使用 10 GB的记忆。那么,创建这么大的数组有什么缺点呢?不过,我可以看到一个优势,那就是您不必担心使用动态数组,这会产生重新分配的成本。

主要缺点是,通过这样做,您对标准库分配器 1 和底层 Linux 分配器的工作原理做出了强有力的假设。事实上,分配器和底层系统并不总是像你提到的那样工作。

现在,您提到了 "copy on write",但您可能真正指的是惰性页面填充和 overcommit 的组合。 根据配置,这意味着您分配但未触及的任何内存可能不计入内存限制并且可能不占用物理内存。

问题是这通常可能行不通。例如:

  • 许多分配器都有接触分配内存的模式,例如,在调试模式下用已知模式填充内存,以帮助诊断对未初始化内存的引用。大多数分配器至少接触分配区域之前的几个字节,以存储可在释放时使用的元数据。因此,您正在对可能会中断的分配器行为做出强有力的假设。
  • Linux 过度使用行为是 totally configurable。实际上,许多服务器端 Linux 用户会禁用它,以减少与 OOM 杀手相关的不确定性和不可恢复的问题。因此,您关于 Linux 行为懒惰的说法仅适用于某些过度使用的配置,而对其他配置则为假。
  • 您可能假设内存以 4K 块的形式提交并围绕它调整您的算法。但是,系统具有不同的页面大小:16K 和 64K 作为基页面大小并不少见,并且 x86 Linux 系统默认启用 transparent huge pages,因此您实际上可能会在没有意识到的情况下获得 2,048K 页面!在这种情况下,您可能最终会提交几乎整个数组,具体取决于您的访问模式。
  • 如评论中所述,"failure mode" 用于此类用途非常差。您认为您只会使用数组的一小部分,但如果您最终使用的数量超出了系统的处理能力,那么充其量您可能会在随机访问新页面时向您的应用程序发出信号,但更像是oom killer 只会杀死你机器上的一些其他随机进程。

1 在这里,我假设您使用 mallocnew 之类的东西来分配数组,因为您没有提到 mmap直接或任何东西。

现实世界的操作系统不仅仅允许您的程序访问所有可用内存 - 它们强制执行配额。因此,在具有足够物理内存的硬件上 运行 的 64 位操作系统将简单地拒绝将所有内存分配给任何程序。如果您的操作系统是虚拟化的(例如,某些管理程序在同一物理平台上托管两个或多个操作系统 - 管理程序对每个托管操作系统强制执行配额,其中一个将为您的程序强制执行配额),则更是如此。

因此,实际上,尝试分配大量内存是一种有效的方法,可以最大限度地提高操作系统不允许您的程序获得所需内存的可能性。

但是,是的,管理员可以增加配额,这也会产生影响。如果您没有管理权限,则需要说服管理员增加这些配额(这不一定容易,除非您的机器只有一个用户)。消耗大量内存的程序可能会导致其他程序内存不足——如果您或其他人需要这些其他程序,这就会成为一个问题。在极端情况下,您的程序可能会使操作系统本身的资源匮乏,这会导致它和它托管的所有程序变慢,并危及系统稳定性。这些问题是系统首先强制执行配额的原因 - 通常是默认设置。

还有一些问题可能会出现,因为操作系统可以配置为过度使用。粗略地说,这意味着当程序请求内存时,操作系统会告诉程序分配已成功,即使操作系统尚未分配它也是如此。随后,当程序使用该内存(通常是向其中写入数据)时,操作系统突然需要实际使内存可用。如果操作系统出于任何原因无法执行此操作,那么程序就会出现问题(程序认为它可以访问内存,但操作系统会阻止访问)。这通常会导致一些影响程序执行的错误情况(并且通常会导致程序终止)。虽然与过度分配相关的问题会影响任何程序,但当程序分配大量内存时,这种可能性会显着增加。