JAVA对象头结构详解
📚前言
大家有没有思考过,java中的对象除了我们自定义的一些属性外,会不会还存在某些我们不了解的东西呢,比如synchronized锁为对象的时候,那么是如何记录锁状态呢?接下来将带领大家解析下对象的神奇之处!
🥠对象内存布局
对象除了我们自定义的一些属性外,在HotSpot虚拟机中,对象在内存中还可以分为三个区域:对象头,实例数据,对齐填充,这三个区域组成起来才是一个完整的对象!
- 对象头:看文章下方解释
- 实例数据: 存放类的属性数据信息,包括父类的属性信息
- 由于虚拟机要求 对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐
🍬对象头
对象头中存储了对象是很多java内部的信息,如hash码,对象所属的年代,对象锁,锁状态标志,偏向锁(线程)ID,偏向时间等,Java对象头一般占有2个机器码
PS:在32位虚拟机中,1个机器码等于4字节,也就是32bit,在64位虚拟机中,1个机器码是8个字节,也就是64bit!
如果对象是数组类型,则需要3个机器码,因为JVM虚拟机可以通过Java对象的元数据信息确定Java对象的大小,但是无法从数组的元数据来确认数组的大小,所以用一块区域用来记录数组长度。 HotSpot虚拟机的对象头包括两部分信息,第一部分为Mark Word,第二部分为class pointer,如果是数组对象,那么还有数组长度!
🍭Mark Word
Mark Word用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。,这部分数据的长度在32位和64位的虚拟机(暂 不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。对象需要存储的运行时数据很多,其实已经超出了32、64位Bitmap结构所能记录的限度,但是对象头信息是与对象自身定的数据无关的额外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间。例如在32位的HotSpot虚拟机 中对象未被锁定的状态下,Mark Word的32个Bits空间中的25Bits用于存储对象哈希码(HashCode),4Bits用于存储对象分代年龄,2Bits用于存储锁标志位,1Bit固定为0,在其他状态(轻量级锁定、重量级锁定、GC标记、可偏向)下对象的存储内容如下表所示。
32位虚拟机
64位虚拟机
🍓实践操作
接下来运用JOL分析运行时对象头状态分析:
引入下方jolmaven依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
<scope>provided</scope>
</dependency>
复制代码
输出对象状态信息
System.out.println( ClassLayout.parseInstance(object).toPrintable() );
复制代码
🥚总结:
今天的对象内存解析就到这了,想更深入理解的小伙伴,可以去阅读下周志明老师的《深入理解Java虚拟机》,这本书里对JVM的一个解析的想当透彻了!