美团面试:什么是Java对象头?底层实现机制?

文摘   2024-11-04 17:08   四川  

关注mikechen十余年BAT架构经验倾囊相授!


大家好,我是mikechen。

最近我们同学被问到了“Java对象头?以及底层实现机制?”,很多同学经常使用new对象,但并不了解背后的实现机制,下面来详解@mikechen


最新mikechen原创超30万字《阿里架构师进阶专题合集》和《大厂最全面试题及答案合集》,请关注本公众号【mikechen的架构笔记】,后台回复:合集,即可领取。

Java对象组成

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

1.对象头

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

2.实例数据

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

3.对齐填充

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


Java对象头

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

对象头主要包括两部分:

  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头存储的信息如下:

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

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锁有着,涉及锁升级,锁优化等。

以上


最后送大家一个福利:

送我原创超30万字阿里架构师进阶专题合集


以及给大家整理最全大厂Java面试题及答案详解,包含:Java、多线程、JVM、Spring、MySQL、Redis、中间件...等必考题答案详解。


需要以上架构专题&面试答案的同学,加我微信即可领取!


添加时备注:资料


mikechen的架构笔记
十余年BAT架构经验倾囊相授!
 最新文章