类指针压缩空间

一、类指针压缩介绍

压缩指针,指的是在 64 位的机器上,使用 32 位的指针来访问数据(堆中的对象或 Metaspace 中的元数据)的一种方式。

对象头中的 Class Pointer 默认占 8 个字节,开启 -XX:+UseCompressedOops 后,为了节省空间压缩为 4 个字节,Java 堆中对象指针会被压缩成 32 位,使用堆基地址(如果堆在低 26G 内存中的话,基地址为 0)。如果堆内存超过 32GB(JVM 是 8 字节对齐),那么压缩指针会失效,因为 32G 内存后,压缩就没有多大必要了,要管理那么大的内存指针也需要很大的宽度。在堆内存小于 32G 时,可以通过编码、解码方式进行优化,使得 JVM 可以支持更大内存配置。当堆内存空间大于 32G 时,压缩指针参数可能会失效,会强制使用 64 位(即 8 字节)来对 Java 对象寻址了。

#开启指针压缩:
-XX:+UseCompressedOops
#关闭指针压缩:
-XX:-UseCompressedOops

但是这个 32GB 是和字节对齐大小相关的,也就是 -XX:ObjectAlignmentInBytes 配置的大小(默认是 8 字节,Java 默认是 8 字节对齐)。-XX:ObjectAlignmentInBytes 可以设置为 8 的整数倍,最大 128,如果设为 24,那么配置最大的堆内存超过 96GB 压缩之指针会失效。

二、_mark 和 _klass 指针

JVM 中,每个对象都有一个指向它自身类的指针,不过这个指针只是指向具体的实现类,而不是接口或者抽象类。只有是 64 位平台上启用了类指针压缩才会存在这个区域。对于 64 位平台,为了压缩 JVM 对象中的 _klass 指针的大小,引入类指针压缩空间(Compressed Class Pointer Space)

_mark:mark word 一组标记,描述了对象的状态,包括对象默认哈希值(如果没有覆盖默认的 hashCode 方法,则哈希值在 hashCode 方法被调用之后,会被记录到 MarkWord 之中)、对象的形状(是否是数组)、锁状态(偏向锁等锁信息,偏向锁在 Java 15 中废弃:Disable and Deprecate Biased Locking)、数组长度(如果标记显示这个对象是数组,描述了数组的长度)。MarkWord 的实现仅仅包含一个 uintptr_t 类型,所以,在 32 位和 64 位虚拟机上面,大小分别是 4 字节和 8 字节。

_klass:是指向对象实现的 Class 的指针。JDK7 之前指向的区域位于持久带(Permanent Generation),JDK8 之后,永久代带废弃,引入了元数据区的概念(Metaspace),所以 JDK8 之后指向的是这个元数据区。这个指针可能是被压缩的,即压缩指针(Compressed OOPs)。当开启对象压缩时占用 4 字节(JVM默认开启),关闭时占用 8 字节

class oopDesc {
    private:
    volatile markWord _mark; // 对象头 mark word
    union _metadata {
        Klass*      _klass; // 类型指针:执行类的指针
        narrowKlass _compressed_klass;
    } _metadata;
}
class markWord {
 private:
  uintptr_t _value;
}
  1. 32 位的 JVM:​​​​​​​
    1. _mark:4 字节。mark word
    2.  _klass:4 字节。指向类的指针,对象的内存布局中的第二个字段(_klass,在 32 位 JVM 中,相对对象内存的位置的偏移量是 4,64 位的是 8)指向的是内存中对象的类定义
  2. 64 位的 JVM:​​​​​​​​​​​​​​
    1. _mark:8 字节
    2. _klass:8 字节
  3. 开启了指针压缩的 64 位的 JVM:
    1.  _mark:8 字节
    2. _klass:4 字节

三、类指针压缩空间

JDK1.8 移除了 permanent generation,class metadata 存储在 native memory (meta space)中,其大小默认是不受限的,可以通过 -XX:MaxMetaspaceSize 来限制。

在 JVM 使用 -XX:+UseCompressedClassPointers 和 -XX:+UseCompressedOops 开启 Compressed Class 的功能后,会在 Metaspace 中开辟出一块新的空间(Compressed Class Space),这个空间不足会出现了 OOM,可以通过设置 -XX:CompressedClassSpaceSize(默认值为1G) 的大小或者 -XX:-UseCompressedClassPointers 来关闭该功能。

如果开启了-XX:+UseCompressedOops 及 -XX:+UseCompressedClassesPointers(默认开启),则UseCompressedOops 会使用 32-bit 的 offset 来代表 java object 的引用,而 UseCompressedClassPointers 则使用32-bit 的 offset 来代表 64-bit 进程中的 class pointer。可以使用 CompressedClassSpaceSize 来设置这块的空间大小,CompressedClassSpace 分配在 MaxMetaspaceSize 里头,即 MaxMetaspaceSize = CompressedClassSpaceSize + Metaspace area (excluding the Compressed Class Space) Size,压缩指针后的内存布局:

指针压缩概要

  1. 64 位平台上默认打开
  2. 使用 -XX:+UseCompressedOops 压缩对象指针
    1. oops 指的是普通对象指针(ordinary object pointer)
    2. Java 堆中对象指针会被压缩成 32 位
    3. 使用堆基地址(如果堆内存低于 26G 时,基地址为 0),即指针的偏移量针对于堆的基地址
  3. 使用 -XX:+UseCompressedClassPointers 选项来压缩类指针
    1. 对象中指向类元数据的指针会被压缩成32位
    2. 使用类指针压缩空间的基地址

四、元空间和类指针压缩空间的区别

类指针压缩空间只包含类的元数据,比如 InstanceKlass, ArrayKlass 仅当打开了 UseCompressedClassPointers 选项才生效。为了提高性能,Java 中的虚方法表也存放到这里。元空间包含类的其它比较大的元数据,比如方法,字节码,常量池(类常量池、运行时常量池)等。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值