我们知道如何制作分块堆,但使用它们会有一些开销。与 32 位 JVM 中更大的堆相比,我们对更快的存储管理的要求更多。如果您真的想要大堆,请切换到 64 位 JVM。我们仍然需要连续的内存, 但是进入 64 位地址空间要容易得多。
上述说法的这个含义是,在 64 位地址空间中更容易获得连续的内存。这是真的?如果是,为什么?
我们知道如何制作分块堆,但使用它们会有一些开销。与 32 位 JVM 中更大的堆相比,我们对更快的存储管理的要求更多。如果您真的想要大堆,请切换到 64 位 JVM。我们仍然需要连续的内存, 但是进入 64 位地址空间要容易得多。
上述说法的这个含义是,在 64 位地址空间中更容易获得连续的内存。这是真的?如果是,为什么?
这是非常真实的。进程必须从虚拟内存地址空间分配内存。它存储代码和数据,其大小受架构寻址能力的限制。在 32 位进程中,您永远不能处理超过 2^32 个字节,这还不包括银行切换技巧。那是 4 GB。操作系统通常也会从中取出一大块,例如在 32 位 Windows 上,它将可寻址 VM 大小减少到 2 GB。
理想情况下,进行分配以使它们紧密结合在一起。这在实践中很少奏效。共享库或 DLL 尤其需要选择一个首选的加载地址,并且必须在构建库时预先猜测。
因此,在实践中,分配是从现有分配之间的孔中进行的,并且您可以获得的最大可能连续分配受到最大孔的大小的限制。通常远小于可寻址的 VM 大小,在 Windows 上通常约为 650 兆字节。随着可用地址空间被分配碎片化,这往往会从那里走下坡路。特别是本机代码无法承受由压缩垃圾收集器移动的分配。如果您使用 Windows,那么您可以使用 SysInternals 的 VMMap 实用程序深入了解 VM 分配。
此问题在 64 位进程中完全消失。理论上可寻址的虚拟内存大小是 2^64,一个巨大的数字。如此之大,以至于当前的处理器无法实现它,它们可以达到 2^48。进一步受到您拥有的操作系统版本及其为那么多 VM 保留页面映射表的意愿的限制。8 TB 是典型的限制。暗示,分配之间的漏洞是巨大的。您的程序将在因 OOM 死亡之前因页面文件抖动而崩溃。
我不能说 JVM 是如何实现的,但是从纯理论的角度来看,如果你有一个更大的虚拟地址空间(例如 64 位与 32 位相比),那么找到一个大的地址空间应该会容易得多可用于分配的连续内存块(走极端 - 您没有机会在 32 位地址空间中找到连续的 4GB 空闲内存,但很有可能在完整的 64 位地址空间中找到该空间地址空间)。
应该注意的是,无论虚拟地址空间大小如何,这仍然将通过分配(可能)不连续的物理内存页面来实现,特别是如果请求的分配很大 - 更大的虚拟地址空间只是意味着有一个可能有更多连续的虚拟地址可供使用。