JVM 知识--对象头

本文最后更新于:12 天前

对象布局 = 对象头(16个byte=128bit) + 实例数据 + 对齐填充 (变成8的倍数)

对象头 = mark word(64bit) + klass pointer(64bit)

klass pointer:指针指向元空间中保存类的模板信息的地址

object header

Common structure at the beginning of every GC-managed heap object. (Every oop points to an object header.) Includes fundamental information about the heap object’s layout, type(类型), GC state (垃圾回收状态), synchronization state (同步状态), and identity hash code (hash code). Consists of two words(字长)(字长和机器有关,64位机字长就是64位). In arrays it is immediately followed by a length field. Note that both Java objects and VM-internal objects have a common object header format.

在每个gc管理的堆对象开始处的公共结构。(每个oop都指向一个对象头)。包括基本信息堆对象的布局、类型,GC状态,同步状态,和哈希值。对象头由两个 word (字长)(字长和机器有关,64位机字长就是64位)。在数组对象中,紧随其后的是长度字段。注意,Java 对象和 JVM 内部对象都有一个通用的对象头格式。

Java 对象头

oop: Ordinary Object Pointer,就是普通对象指针。

启用CompressOops后,会压缩的对象:

  • 每个Class的属性指针(静态成员变量);

  • 每个对象的属性指针;

  • 普通对象数组的每个元素指针。

|--------------------------------------------------------------------------------------------------------------|
|                                              Object Header (128 bits)                                        |
|--------------------------------------------------------------------------------------------------------------|
|                        Mark Word (64 bits)                                    |      Klass Word (64 bits)    |       
|--------------------------------------------------------------------------------------------------------------|
|  unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |     OOP to metadata object   |   无锁 01
|----------------------------------------------------------------------|--------|------------------------------|
|  thread:54 |         epoch:2      | unused:1 | age:4 | biased_lock:1 | lock:2 |     OOP to metadata object   |  偏向锁 01
|----------------------------------------------------------------------|--------|------------------------------|
|                     ptr_to_lock_record:62                            | lock:2 |     OOP to metadata object   |  轻量锁 00
|----------------------------------------------------------------------|--------|------------------------------|
|                     ptr_to_heavyweight_monitor:62                    | lock:2 |     OOP to metadata object   |  重量锁 10
|----------------------------------------------------------------------|--------|------------------------------|
|                                                                      | lock:2 |     OOP to metadata object   |    GC 11
|--------------------------------------------------------------------------------------------------------------|

对象头

注意:

  1. 为什么要这么实现?

    因为对象头信息是跟对象自身定义的数据结构无关的。这些信息所记录的状态是用于JVM对对象的管理的。更重要的是,不同状态的存储内容基本上是互斥的。所以基于节省空间的角度考虑,Mark Word 被设计成动态的。

  2. JVM 默认大端存储

    可以使用 jol 打印对象信息,不过注意,JVM默认是大端存储,所以注意顺序是锁标志位在输出的第一个八位数据的最后两位

    例如:00000101 是打印的第一个字节,其中最后的101表示偏向锁

  3. 当对象的状态不是默认状态时,对象的hashcode不存储在对象头

    当是轻量级锁/重量级锁时,JVM 会将对象的 mark word 复制一份到栈帧的 Lock Record 中。等线程释放该对象时,再重新复制给对象。

  4. 如果一个对象头中存在 hashcode,则无法使用偏向锁。

    即:如果在锁对象加锁前计算了 hashCode ,且 hashCode 方法没有被重写,那么会把 hashCode 存储到 Mark Word 中,加锁不会使用偏向锁,而是轻量级锁。