由于Java面向对象的思想,在JVM中需要大量存储对象,存储时为了实现一些额外的功能,需要在对象中添加一些标记字段用于增强对象功能,这些标记字段组成了对象头。
首先了解一下Java对象内存布局:
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
一个java对象头包含了2个word,并且好包含了堆对象的布局、类型、GC状态、同步状态和标识哈希码。
java的对象头由以下三部分组成:
1、Mark Word
2、klass Word(指向类的指针)
3、array length(数组长度)(只有数组对象才有)
在64位系统下,存放Class指针的空间大小是8字节,MarkWord是8字节,对象头为16字节。
但在64位开启指针压缩的情况下,存放Class指针的空间大小是4字节,MarkWord是8字节,对象头为12字节。
Mark Word为第一个word根据文档可以知他里面包含了锁的信息,hashcode,gc信息等等
Mark Word在32位JVM中的长度是32bit,在64位JVM中长度是64bit。
这篇文章主讲jvm64位的
Mark Word示意图如下:
其中各部分的含义如下:
(1)lock:2位的锁状态标记位,由于希望用尽可能少的二进制位表示尽可能多的信息,所以设置了lock标记。
(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:25位的对象标识Hash码,采用延迟加载技术。调用方法System.identityHashCode()计算,并会将结果写到该对象头中。当对象被锁定时,该值会移动到管程Monitor中。
(5)thread:持有偏向锁的线程ID。
(6)epoch:偏向时间戳。
(7)ptr_to_lock_record:指向栈中锁记录的指针。
(8)ptr_to_heavyweight_monitor:指向管程Monitor的指针。
klass Word为对象头的第二个word主要指向对象的元数据
为了节约内存默认开启s开启指针压缩
如果对象是一个数组, 那在对象头中还必须有一块数据用于记录数组长度.
1、添加JOL依赖:JOL来分析java的对象布局
<dependencies>
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
</dependencies>
2、实例
/**
* 对象A
*/
public class A {
//boolean flag =false;
}
/**
* 打印Java对象头
*/
public class JoLExample {
public static void main(String[] args) throws Exception {
A a = new A();
//jvm的信息
System.out.println(VM.current().details());
System.out.println("-------------------------");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
4、分析结果
1、Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]对应:[Oop(Ordinary Object Pointer), boolean, byte, char,
short, int, float, long, double]大小
整个对象一共16B,其中对象头(Object header)12B,还有4B是对齐的字节(因为在64位虚拟机上对象的大小必
须是8的倍数),由于这个对象里面没有任何字段,故而对象的实例数据为0B?
两个问题:
1、什么叫做对象的实例数据呢?
2、那么对象头里面的12B到底存的是什么呢?
首先要明白什么对象的实例数据很简单,我们可以在A当中添加一个boolean的字段,大家都知道boolean字段占
1B,然后再看结果:
整个对象的大小还是没有改变一共16B,其中对象头(Object header)12B,boolean字段flag(对象的实例数据)占
1B、剩下的3B就是对齐字节。
由此我们可以认为一个对象的布局大体分为三个部分分别是
1、对象头(Object header)
2、对象的实例数据
3、字节对齐