Object o = new Object()占多少个字节?-对象的内存布局 (2)

首先,我们来看下,JDK8 Hotspot实现64位虚拟机,是不是会默认开启压缩类指针压缩对象指针

win + R,输入cmd,敲入下面的命令java -version,相信大家对这个命令很熟悉了,查看java版本

Object o = new Object()占多少个字节?-对象的内存布局

接下来我们加个参数-XX:+PrintCommandLineFlags,这个参数让JVM打印出那些已经被用户或者JVM设置过的详细的XX参数的名称和值,注意看下面两个参数

Object o = new Object()占多少个字节?-对象的内存布局

-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,我们将对象的内部信息打印出来:

Object o = new Object()占多少个字节?-对象的内存布局

可以看到有 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。我们再看看结果:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppwzy.html