首先,我们来看下,JDK8 Hotspot实现的64位虚拟机,是不是会默认开启压缩类指针和压缩对象指针。
win + R,输入cmd,敲入下面的命令java -version,相信大家对这个命令很熟悉了,查看java版本
接下来我们加个参数-XX:+PrintCommandLineFlags,这个参数让JVM打印出那些已经被用户或者JVM设置过的详细的XX参数的名称和值,注意看下面两个参数
-XX:+UseCompressedClassPointers:使用压缩类指针
-XX:+UseCompressedOops:使用压缩普通对象指针
可以看到,这两个配置是默认开启的。
注意:32位HotSpot VM是不支持UseCompressedOops参数的,只有64位HotSpot VM才支持。
什么是oop?这参数后面的oop可不是面向对象编程Object Oriented Programming的意思,而是普通对象指针Ordinary Object Pointer。
启用UseCompressedOops后,会压缩的对象:
每个Class的属性指针(静态成员变量);
每个对象的属性指针;
普通对象数组的每个元素指针。
当然,压缩也不是所有的指针都会压缩,对一些特殊类型的指针,JVM是不会优化的,例如指向PermGen的Class对象指针、本地变量、堆栈元素、入参、返回值和NULL指针不会被压缩。
关于UseCompressedClassPointers和UseCompressedOops这样一看,好像UseCompressedOops对Object的内存布局并没有影响,其实不然,开启UseCompressedOops,默认会开启UseCompressedClassPointers,会压缩klass pointer这部分的大小,由8字节压缩至4字节,间接的提高内存的利用率。关闭UseCompressedOops默认会关闭UseCompressedClassPointers。
如果开启UseCompressedClassPointers,根据上面的条件,结果跟只开启UseCompressedOops一样,会在内存中消耗20个字节,o指针占4个字节,Object对象占16个字节。
如果关闭UseCompressedClassPointers,根据上面的条件,UseCompressedOops还是会开启,会在内存中消耗20个字节,o指针占4个字节,Object对象占16个字节。
如果开启类指针压缩,+UseCompressedClassPointers,并关闭普通对象指针压缩,-UseCompressedOops,此时会报错,UseCompressedClassPointers requires UseCompressedOops。因为UseCompressedClassPointers的开启是依赖于UseCompressedOops的开启。
2.验证实例对象布局大小上面已经看到,JVM默认开启了压缩类指针和压缩普通对象指针,那么在这个情况下,new Object()是否真的是8字节(Mark Word) + 4字节(Klass Pointer) + 4字节(Padding) = 16字节呢?
还好 openjdk 给我们提供了一个工具包,可以用来获取对象的信息和虚拟机的信息,我们只需引入 jol-core 依赖,如下:
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.14</version> </dependency>jol-core 常用的三个方法:
ClassLayout.parseInstance(object).toPrintable():查看对象内部信息.
GraphLayout.parseInstance(object).toPrintable():查看对象外部信息,包括引用的对象.
GraphLayout.parseInstance(object).totalSize():查看对象总大小.
简单对象为了简单化,我们不用复杂的对象,自己创建一个类 Test01,先看无属性字段的时候
public class Test01 { public static void main(String[] args) { Test01 t = new Test01(); System.out.println(ClassLayout.parseInstance(t).toPrintable()); } }通过 jol-core 的 api,我们将对象的内部信息打印出来:
可以看到有 OFFSET、SIZE、TYPE DESCRIPTION、VALUE 这几个名词头,它们的含义分别是
OFFSET:偏移地址,单位字节;
SIZE:占用的内存大小,单位为字节;
TYPE DESCRIPTION:类型描述,其中object header为对象头;
VALUE:对应内存中当前存储的值,二进制32位;
同时可以看到,t实例对象共占据16Byte,object header占据12Byte,其中mark word占8Byte,klass pointer占4Byte,padding占4Byte。
如果我把压缩普通对象指针的参数去掉呢?可以通过配置vm参数关闭压缩类指针,-XX:-UseCompressedClassOops。我们再看看结果: