查看原文
其他

JVM中的压缩OOP

Emma SpringForAll社区 2021-05-27

1 简介

JVM为我们管理内存,这减轻了开发人员的内存管理负担,因此我们不需要手动操作对象指针,这被证明是耗时且容易出错的。

在幕后,JVM结合了许多漂亮的技巧来优化内存管理过程。一个技巧是使用压缩指针,我们将在本文中对其进行评估。首先,让我们看看JVM如何在运行时表示对象。

2 运行时表示对象

HotSpot JVM使用名为oops或Ordinary Object Pointers的数据结构来表示对象。这些oops等同于本地C指针。instanceOops是一种特殊的oop,表示Java中的对象实例。此外,JVM还支持其他少数oops在OpenJDK source tree保存。

让我们看看在JVM中instanceOops在内存中如何分布。

2.1 对象内存布局

instanceOop的内存布局很简单:它只是对象头后面紧跟零个或多个对实例字段的引用。

JVM中对象头对象头表示包括:

  • 标记词,用于许多目的,如偏置锁定,身份哈希值和GC。它不是一个oop,但由于历史原因,它存在于OpenJDK的oop源代码树中。

  • Klass词,表示指向类元数据的指针。在Java 7之前,它们是在永久代中保留的,但是从Java 8开始,它们就在Metaspace中。

  • 32位长度字,仅数组字段,表示数组长度。

  • 32位的间隔来强制对象对齐。这会让事情变得更容易,我们稍后会看到。

在头部之后,对实例字段有零个或多个引用。在这种情况下,单字就是本机机器字,因此在传统的32位机器上为32位,在更现代的系统上为64位。

2.2 垃圾剖析

假设我们要从传统的32位架构切换到更现代的64位机器。起初,我们可能希望立即获得性能提升。但是,当涉及JVM时并非总是如此。

这种可能性能下降的主要原因是64位对象引用。64位引用比32位引用的多占用两倍空间,因此这会导致更多的内存消耗和更频繁的GC周期。专用于GC周期的时间越多,应用程序线程的CPU执行切片就越少。

那么,我们应该切换回来并再次使用这些32位架构吗?即使这是一个选项,我们无论多做多少工作也无法在32位进程空间中拥有超过4 GB的堆空间。

3 压缩OOPs

事实证明,JVM可以通过压缩对象指针或oops来避免浪费内存,因此我们可以充分利用这两个方面:在64位计算机中允许超过4 GB的堆空间和32位引用!

3.1 基本优化

正如我们之前看到的,JVM为对象进行填充,使其大小变为8个字节的倍数。使用这些填充后,oops中的最后三位始终为零。这是因为在二进制中8的倍数的数字总是以000结尾。

由于JVM已经知道最后三位始终为零,因此在堆中存储那些零是没有意义的。相反,它假设它们存在并存储3个其他更重要的位,我们以前无法将其装入32位。现在,我们有一个带有3个右移零的32位地址,所以我们将35位指针压缩成32位指针。这意味着我们可以在不使用64位引用的情况下使用最多32 GB – 2^(32+3)=2^35=32 GB的堆空间。

为了使这种优化工作,当JVM需要在内存中找到一个对象时,它将指针向左移动3位(基本上将这些3-zero重新加到最后)。另一方面,当向堆加载指针时,JVM将指针向右移动3位以丢弃先前添加的零。基本上,JVM执行更多的计算以节省一些空间。幸运的是,对于大多数CPU来说,位移是一个非常简单的操作。

要启用oop压缩,我们可以使用标志-XX:+ UseCompressedOops进行调整。只要最大堆大小小于32 GB,Java 7以上的默认开启oop压缩。当最大堆大小超过32 GB时,JVM将自动关闭oop压缩。因此,需要以不同方式管理超过32 Gb堆大小的内存利用率。

3.2 超过32 GB

当Java堆大小大于32GB时,也可以使用压缩指针。虽然默认对象对齐是8个字节,但可以使用-XXObjectAlignmentInBytes配置字节值。指定的值应为2的幂,并且必须在8和256的范围内。

我们可以使用压缩指针计算最大可能的堆大小,如下所示:

  1. 4 GB * ObjectAlignmentInBytes

例如,当对象对齐为16个字节时,通过压缩指针最多可以使用64 GB的堆空间。

请注意,随着对齐值的增加,对象之间未使用的空间也会增加。因此,我们可能没有意识到使用大Java堆大小的压缩指针会带来什么好处。

3.3 未来的GCS

ZGC是Java 11中的新增功能,是一个实验性和可扩展的低延迟垃圾收集器。它可以处理不同范围的堆大小,同时保持GC暂停低于10毫秒。由于ZGC需要使用64位彩色指针,因此它不支持压缩引用。因此,使用像ZGC这样的超低延迟GC必须使用更多内存再次进行交易。

4 结束语

在本文中,我们描述了64位体系结构中的JVM内存管理问题。我们了解了压缩指针和对象对齐,并了解了JVM如何解决这些问题,允许我们使用更大的堆减少浪费的指针和获得最少的额外计算。

原文链接:https://www.baeldung.com/jvm-compressed-oops

作者:Ali Dehghani

译者:Emma


推荐:Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现

上一篇:Spring Boot RabbitMQ 四种交换器





 关注公众号

点击原文阅读更多


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存