【问题标题】:Trick behind JVM's compressed OopsJVM 压缩 Oops 背后的技巧
【发布时间】:2014-09-27 00:53:12
【问题描述】:

所以我知道 HotSpot VM 现在默认启用压缩 oops。从 Java SE 6u23 开始,它通过 VM 选项 -XX:+UseCompressedOops 对此提供了支持。我知道它允许有效地利用 CPU 缓存,因为 CPU 缓存可以容纳更多的引用,而不是它们必须处理 64 位大小的引用。但我不明白的是,仅使用 32 位 JVM 可以寻址多达 264 个地址。

为了简化问题,我们如何仅使用 2 位寻址多达 24 个内存地址?这种地址方案的可能编码/解码是什么?

【问题讨论】:

  • 提示:CompressedOops 限制为 2^35 个地址,而不是 2^64 个。

标签: java memory-management jvm


【解决方案1】:

有关压缩 oops 的详细说明,请参阅 John Rose @Oracle 的 "Compressed oops in the Hotspot JVM" 文章。

TL;DR 版本是:

  • 在现代计算机体系结构中,内存地址是字节地址,
  • Java 对象引用是指向单词开头的地址1,
  • 在 64 位机器上,字对齐意味着对象引用/地址的低 3 位为零2
  • 因此,通过将地址右移 3 位,我们可以将 64 位地址的最多 35 位“压缩”为 32 位字,
  • 并且,解压缩可以通过向左移动 3 位来完成,这会将这 3 个零位放回原处,
  • 35 位寻址允许我们使用适合 64 位机器上 32 位(半)字的压缩 oop 来表示最多 32 GB 堆内存的对象指针。

请注意,这适用于 64 位 JVM。我们仍然需要能够寻址包含(最多)32 GB 堆的内存1,这意味着 64 位硬件地址(在现代 CPU/计算机架构上)。

还请注意,这样做会有一点点惩罚;即在常规引用和压缩引用之间转换所需的移位指令。但是,另一方面是消耗的实际内存更少3,因此内存缓存通常更有效。

1 - 这是因为现代计算机架构针对字对齐内存访问进行了优化。

2 - 这假设您没有使用 -XX:ObjectAlignmentInBytes 来增加其默认(和最小)值 8 字节的对齐方式。

3 - 事实上,内存节省是特定于应用程序的。它取决于平均对象对齐损耗、参考与非参考字段的比率等。如果您考虑调整对象对齐,它会变得更加复杂。


为了简化问题,我们如何仅使用 2 位寻址多达 24 个内存地址?这种地址方案的可能编码/解码是什么?

您不能寻址 24 字节地址。但是您可以使用 2 位字地址寻址 22 个字地址(假设为 32 位字)。如果您可以假设所有字节地址都是字对齐的,那么您可以将 4 位字节地址压缩为 2 位字地址,方法是将其移动 2 位位置。

【讨论】:

  • 我不遵循“在 64 位机器上,这意味着地址的低 3 位为零”的逻辑。为什么引用指向单词开头的事实会导致底部3位都是0s的情况?
  • 因为现代计算机体系结构是字节寻址的。
  • 知道了。我也很难理解你所说的“所以,通过将地址向左移动 3 位,我们可以将 64 位地址的 35 位“压缩”为 32 位字,”。你到 35 号了吗?
  • 简单算术。 32 + 3 == 35。
  • 我想知道他们为什么不选择 16 字节对齐?大多数应用程序不会使用大量没有字段的裸对象;即使是单个布尔字段也会将对象大小增加到 16 个字节。
【解决方案2】:

它不适用于 32 位 JVM。这是为了减轻 64 位 JVM 中出现的额外开销。我认为 Oracle 的页面解释得很好:

压缩的糟糕

Compressed oops 表示托管 指针(在 JVM 软件中的许多但不是所有地方)为 32 位 对象相对于 64 位 Java 堆基地址的偏移量。因为他们是 对象偏移量而不是字节偏移量,它们可用于解决 最多 40 亿个对象(不是字节),或最大堆大小 32 GB。要使用它们,它们必须按 8 倍缩放,并且 添加到 Java 堆基地址以查找它们所指向的对象 参考。

source

【讨论】:

    【解决方案3】:

    https://blog.codecentric.de/en/2014/02/35gb-heap-less-32gb-java-jvm-memory-oddities/

    我认为这篇文章很好地解释了这一点。带走:

    因为 JVM 内存布局使用 8 字节寻址方案,这意味着对象可以位于地址 0、8、16、24……但不能位于 2、7 或任何其他非 8 的倍数,压缩的 oops 只是寻址虚拟位置 0, 1, 2, 3 而不是真实的 0, 8, 16, 24。要从压缩地址到真实地址,JVM 只需将其左移 3 次。很简单。

    4G * 8 = 32

    【讨论】:

      【解决方案4】:

      CompressedOops.

      来自文章: 并非所有指针都被压缩,压缩后的指针是 32 位值,必须按 8 倍缩放并添加到 64 位基地址才能找到它们所引用的对象。

      现在请注意,您无法使用这些 32 位指针寻址 2^64 位内存,但您可以使用它们访问大量 Objects。如果你在内存位置x 有一个对象,那么你不可能在x+1 有另一个对象。这就是为什么您不需要访问每个内存位置的原因。

      【讨论】:

        猜你喜欢
        • 2020-04-04
        • 2022-01-22
        • 2023-03-17
        • 2013-04-21
        • 1970-01-01
        • 2014-03-10
        • 1970-01-01
        • 2010-11-12
        • 1970-01-01
        相关资源
        最近更新 更多