Java对象头结构图解(MarkWord等全面解析)

Java对象头结构图解(MarkWord等全面解析)-mikechen

Java是一个面向对象的语言,在Java中可以使用new关键字来产生一个对象,但这个对象到底是什么?下面详解@mikechen

Java对象组成

在HotSpot虚拟机中,真实的Java对象是分成三个部分:

Java对象头结构图解(MarkWord等全面解析)-mikechen

1.对象头

对象头:用于存储对象自身运行时数据,包括哈希值(hashcode)、类型、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。

2.实例数据

实例数据:用于存放类的数据信息,父类的信息,对象字段属性信息

3.对齐填充

对象的填充字节 :在JVM中,要求对象占用内存的大小应该是8bit的倍数,这个信息是用来补齐8bit的

 

Java对象头

对象头是Java中对象都具有的属性,是jvm在编译和运行阶段读取的信息。

对象头主要包括两部分:

Java对象头结构图解(MarkWord等全面解析)-mikechen

  1. Mark Word
  2. 类型指针

其中最复杂的是MarkWordMarkWord用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。

标记字段(Mark Word)

MarkWord用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。

MarkWord占用空间大小根据JVM决定,为JVM的一个字大小,也就是32位JVM中Mark Word占用4个字节,64位JVM中占用8个字节64位。

为了节省空间,Mark Word是以非固定的数据结构来存储,具体数据结构如下:

1)在32位的环境中,Java头存储的信息如下:

Java对象头结构图解(MarkWord等全面解析)-mikechen

2)在64位的环境中,Java头存储的信息如下:

Java对象头结构图解(MarkWord等全面解析)-mikechen

3)参数介绍,如下:

「锁标志位(lock):」区分锁状态,这个参数总共占2bit,可以表示四种状态,但是上面图中,锁状态有五种,可以看出,无锁态 和 偏向锁都用 01 表示。那怎么区分无锁态跟偏向锁?这时引入 是否偏向锁 参数。

「是否偏向锁(biased_lock):」是否偏向锁,这个参数占 1bit,0表示 不是偏向锁,1表示 是偏向锁

「分代年龄(age):」表示java对象被GC的次数,每次GC的时候,如果对象在Survivor区复制一下,年龄增加1。当对象达到设定的阀值时,将会晋升到老年代。这个参数占 4bit,也就是最大值是 2^4 – 1 = 15。这是JVM参数XX:MaxTenuringThreshold选项最大值为15的原因。默认情况下并行GC的年龄阀值为15,并发GC的年龄阀值为6。

「hashCode:」对象的hashCode,使用方法System.identityHashCode()计算,采用延迟计算,计算后会把结果写到该对象头中。当对象被锁定时,该值会移动到Monitor中。

「线程ID:」在偏向模式中,当某个线程持有该对象,则该对象头的 线程ID位置存储的是这个线程的ID。这样在后面的操作中,就不需要再进行获取锁的动作

「epoch:」偏向锁时间戳,用于在CAS锁操作过程中,偏向性标识,表示更偏向哪个锁

「ptr_to_lock_record:」在轻量级锁的状态下,指向栈中锁记录的指针。当锁获取是无竞争时,JVM使用原子操作而不是OS互斥。这种技术称为轻量级锁定。在轻量级锁定的情况下,JVM通过CAS操作在对象头中设置指向锁记录的指针

「ptr_to_heavyweight_monitor:」在重量级锁的状态下,指向管程Montior的指针。如果两个不同的线程同时在同一个对象上竞争,则必须将轻量级锁定升级到Monitor心管理等待的线程。在重量级锁定的情况下,JVM设置ptr_to_heavyweight_monitor指向Montior。

类型指针(Klass Pointer)

这个字段存储对象的类元数据的指针,JVM通过这个指针来确定这个对象是哪个类的实例,占用JVM一个 字大小,即32bit JVM占4字节,64bit JVM占8字节。

Java对象头总结

通过对象在内存中的布局分析,我们可以明白一些问题的底层解释,比如:

  1. 如何计算java对象在内存中占用空间大小:可以从java对象头、实体数据、填充数据三部分来计算;
  2. JVM如何获取对象的GC年龄:在对象头Mark Word有一个分代年龄(age)来记录对象的GC年龄;
  3. 为啥最大GC15次后,对象就会被移动老年代:因为分代年龄(age)字段占用空间大小是4bit,也就是15,这个32bit还是64bit都是一样的。

另外我们发现对象头中有很多锁状态相关的字段,这个主要跟synchronized锁有着,涉及锁升级,锁优化等,这个可以参考:Synchronized的底层实现原理详解

作者简介

陈睿|mikechen,10年+大厂架构经验,BAT资深面试官,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

👇阅读更多mikechen架构文章👇

阿里架构 |双11秒杀 |分布式架构 |负载均衡 |单点登录 |微服务 |云原生 |高并发 |架构师

以上

关注作者「mikechen」公众号,获取更多技术干货!

后台回复架构,即可获取《阿里架构师进阶专题全部合集》,后台回复面试即可获取《史上最全阿里Java面试题总结

评论交流
    说说你的看法