对象头包括三个字段,第一个字段叫作Mark Word(标记字),用于存储自身运行时的数据,例如GC标志位、哈希码、锁状态等信息。第二个字段叫作Class Pointer(类对象指针),用于存放方法区Class对象的地址,虚拟机通过这个指针来确定这个对象是哪个类的实例;
第三个字段叫作Array Length(数组长度)。如果对象是一个Java数组,那么此字段必须有,用于记录数组长度的数据;如果对象不是一个Java数组,那么此字段不存在,所以这是一个可选字段;
对象体包含对象的实例变量(成员变量),用于成员属性值,包括父类的成员属性值。这部分内存按4字节对齐;
对齐字节也叫作填充对齐,其作用是用来保证Java对象所占内存字节数为8的倍HotSpot VM的内存管理要求对象起始地址必须是8字节的整数倍。对象头本身是8的数,当对象的实例变量数据不是8的倍数时,便需要填充数据来保证8字节的对齐;
接下来,对Object实例结构中几个重要的字段的作用做一下简要说明:
主要用来表示对象的线程锁状态,另外还可以用来配合GC存放该对象的hashCode。
一个指向方法区中Class信息的指针,意味着该对象可随时知道自己是哪个Class的实例。
占用32位(在32位JVM中)字节,这是可选的,只有当本对象是一个数组对象时才会有这个部分。
用于保存对象属性值,是对象的主体部分,占用的内存空间大小取决于对象的属性数量和类型。
不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。当对象实例数据部分没有对齐(8字节的整数倍)时,就需要通过对齐填充来补全。
·Class对象的属性指针(静态变量)。
·Object对象的属性指针(成员变量)。
·普通对象数组的元素指针。
开启压缩或关闭压缩:
java -XX:+UseCompressedOops mainclass
java -XX:-UseCompressedOops mainclass
Mark Word的结构信息
(1)lock:锁状态标记位,占两个二进制位,由于希望用尽可能少的二进制位表示尽可能多的信息,因此设置了lock标记。该标记的值不同,整个Mark Word表示的含义就不同。
(2)biased_lock:对象是否启用偏向锁标记,只占1个二进制位。为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。
(3)age:4位的Java对象分代年龄。在GC中,对象在Survivor区复制一次,年龄就增加1。当对象达到设定的阈值时,将会晋升到老年代。默认情况下,并行GC的年龄阈值为15,并发GC的年龄阈值为6。由于age只有4位,因此最大值为15,这就是-XX:MaxTenuringThreshold选项最大值为15的原因。
(4)identity_hashcode:31位的对象标识HashCode(哈希码)采用延迟加载技术,当调用Object.hashCode()方法或者System.identityHashCode()方法计算对象的HashCode后,其结果将被写到该对象头中。当对象被锁定时,该值会移动到Monitor(监视器)中。
(5)thread:54位的线程ID值为持有偏向锁的线程ID。
(6)epoch:偏向时间戳。
(7)ptr_to_lock_record:占62位,在轻量级锁的状态下指向栈帧中锁记录的指针。
(8)ptr_to_heavyweight_monitor:占62位,在重量级锁的状态下指向对象监视器的指针。使用JOL工具查看对象的布局
<!--Java Object Layout -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.11</version>
</dependency>