$ jdk8-64/java -jar jol-cli.jar internals java.lang.Object # Running 64-bit HotSpot VM. # Using compressed oop with 3-bit shift. # Using compressed klass with 3-bit shift. # Objects are 8 bytes aligned.
Instantiated the sample instance via default constructor.
java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 # Mark word 4 4 (object header) 00 00 00 00 # Mark word 8 4 (object header) 00 10 00 00 # (not mark word) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
它显示前 12 个字节是对象头。不幸的是,它没有更详细地解析其内部结构,因此,我们需要深入研究 Hotspot 源代码以弄清楚这一点。在这里,你会注意到对象头包括两部分:mark word 和 class word。Class word 包含对象类型的信息:它链接到描述该类的本机结构。我们将在下一节中讨论这一部分。其余的元数据保存在 mark word 中。
mark word 有多种用途:
存储用于 GC 的元数据(分代年龄和转发数据)
存储身份标识 hash code
存储锁标志位信息
请注意,每个对象都必须有一个 mark word,因为它处理每个 Java 对象特有的事物。
5.1 存储用于 GC 的转发数据
GC 需要移动对象时,它们至少需要临时记录对象的新位置。mark word 将对此进行编码,以用于 GC 代码,以协调重定位和更新引用工作。这会将 mark word 锁定为与 Java 引用一样宽。GC 转发数据在 mark word 中实现所需要的最小内存量:32 位平台为 4 字节,而 64 位平台为 8 字节。
不幸的是,我们无法显示展示来自 Java 应用程序(而 JOL 是 Java 应用程序)进行 GC 转发的 mark word,因为要么在我们取消阻止 full GC 时就已经消失了。要么并发 GC 障碍阻止我们看到旧对象。
$ jdk8-64/bin/java -cp jol-samples.jar org.openjdk.jol.samples.JOLSample_15_IdentityHashCode # Running 64-bit HotSpot VM. # Using compressed oop with 3-bit shift. # Using compressed klass with 3-bit shift.
**** Fresh object org.openjdk.jol.samples.JOLSample_15_IdentityHashCode$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 97 ef 00 f8 (10010111 11101111 00000000 11111000) (-134156393) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
hashCode: 2f333739
**** After identityHashCode() org.openjdk.jol.samples.JOLSample_15_IdentityHashCode$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 39 37 33 (00000001 00111001 00110111 00110011) (859257089) 4 4 (object header) 2f 00 00 00 (00101111 00000000 00000000 00000000) (47) 8 4 (object header) 97 ef 00 f8 (10010111 11101111 00000000 11111000) (-134156393) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
请注意,哈希码值为 2f333739。计算机内部为小端字节序,低位字节在前,高位字节在后,可以使用代码 ByteOrder.nativeOrder() 查看系统机器字节序。你现在可以在对象头中找到它的十六进制:01 39 37 33 2f。 01 是 mark word 标记,其余是用 little-endian 小端字节序编写的身份哈希码。而且,我们还有 3 个字节的备用空间!
5.4 锁信息
Java 同步采用了复杂的状态机。由于每个 Java 对象都可以使用 synchronized,因此锁定状态应与任何 Java 对象相关联。mark word 维护了大部分状态。
$ jdk8-64/bin/java -cp jol-samples.jar org.openjdk.jol.samples.JOLSample_13_BiasedLocking # Running 64-bit HotSpot VM. # Using compressed oop with 3-bit shift. # Using compressed klass with 3-bit shift.
**** Fresh object org.openjdk.jol.samples.JOLSample_13_BiasedLocking$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) # 无锁 4 4 (object header) 00 00 00 00 8 4 (object header) c0 07 08 00 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
**** With the lock org.openjdk.jol.samples.JOLSample_13_BiasedLocking$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 b0 00 80 (00000101 11010000 00000000 01111011) # 偏向锁 4 4 (object header) b8 7f 00 00 # 偏向锁 8 4 (object header) c0 07 08 00 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
**** After the lock org.openjdk.jol.samples.JOLSample_13_BiasedLocking$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 b0 00 80 (00000101 11010000 00000000 01111011) # 偏向锁 4 4 (object header) b8 7f 00 00 # 偏向锁 8 4 (object header) c0 07 08 00 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
$ jdk8-64/bin/java -cp jol-samples.jar org.openjdk.jol.samples.JOLSample_14_FatLocking # Running 64-bit HotSpot VM. # Using compressed oop with 3-bit shift. # Using compressed klass with 3-bit shift.
**** Fresh object org.openjdk.jol.samples.JOLSample_14_FatLocking$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) # 无锁 4 4 (object header) 00 00 00 00 8 4 (object header) 5a ef 00 f8 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
**** Before the lock org.openjdk.jol.samples.JOLSample_14_FatLocking$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 58 29 ad 04 (01011000 00101001 10101101 00000100) # 轻量级锁 4 4 (object header) 00 70 00 00 8 4 (object header) 5a ef 00 f8 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
**** With the lock org.openjdk.jol.samples.JOLSample_14_FatLocking$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 4a c1 80 e1 (01001010 11000001 10000000 11100001) # 重量级锁 4 4 (object header) f6 7f 00 00 8 4 (object header) 5a ef 00 f8 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
**** After the lock org.openjdk.jol.samples.JOLSample_14_FatLocking$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 4a c1 80 e1 (01001010 11000001 10000000 11100001) # 重量级锁 4 4 (object header) f6 7f 00 00 8 4 (object header) 5a ef 00 f8 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
**** After System.gc() org.openjdk.jol.samples.JOLSample_14_FatLocking$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 09 00 00 00 (00001001 00000000 00000000 00000000) # 锁释放 4 4 (object header) 00 00 00 00 8 4 (object header) 5a ef 00 f8 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
$ jdk8-64/bin/java -cp jol-samples.jar org.openjdk.jol.samples.JOLSample_26_IHC_BL_Conflict # Running 64-bit HotSpot VM. # Using compressed oop with 3-bit shift. # Using compressed klass with 3-bit shift.
**** Fresh object org.openjdk.jol.samples.JOLSample_26_IHC_BL_Conflict$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) # No lock 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) 8 4 (object header) 5a ef 00 f8 (01011010 11101111 00000000 11111000) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
**** With the lock org.openjdk.jol.samples.JOLSample_26_IHC_BL_Conflict$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 e0 80 c7 (00000101 11100000 10000000 11000111) # Biased lock 4 4 (object header) f7 7f 00 00 (11110111 01111111 00000000 00000000) # Biased lock 8 4 (object header) 5a ef 00 f8 (01011010 11101111 00000000 11111000) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
**** After the lock org.openjdk.jol.samples.JOLSample_26_IHC_BL_Conflict$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 e0 80 c7 (00000101 11100000 10000000 11000111) # Biased lock 4 4 (object header) f7 7f 00 00 (11110111 01111111 00000000 00000000) # Biased lock 8 4 (object header) 5a ef 00 f8 (01011010 11101111 00000000 11111000) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
hashCode: 4cc77c2e
**** After the hashcode org.openjdk.jol.samples.JOLSample_26_IHC_BL_Conflict$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 2e 7c c7 (00000001 00101110 01111100 11000111) # Hashcode 4 4 (object header) 4c 00 00 00 (01001100 00000000 00000000 00000000) # Hashcode 8 4 (object header) 5a ef 00 f8 (01011010 11101111 00000000 11111000) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
**** With the second lock org.openjdk.jol.samples.JOLSample_26_IHC_BL_Conflict$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 80 59 9e 02 (10000000 01011001 10011110 00000010) # Lightweight lock 4 4 (object header) 00 70 00 00 (00000000 01110000 00000000 00000000) # Lightweight lock 8 4 (object header) 5a ef 00 f8 (01011010 11101111 00000000 11111000) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
**** After the second lock org.openjdk.jol.samples.JOLSample_26_IHC_BL_Conflict$A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 2e 7c c7 (00000001 00101110 01111100 11000111) # Hashcode 4 4 (object header) 4c 00 00 00 (01001100 00000000 00000000 00000000) # Hashcode 8 4 (object header) 5a ef 00 f8 (01011010 11101111 00000000 11111000) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
刚开始对象通过偏向锁定的宽限期后为无锁状态,可偏向(锁标记为 …101)。第一次进入代码同步块时,偏向锁通过 CAS 操作将线程 ID 置换到 mark word 中。在释放锁后,偏向锁仍然会保留在对象头中,以便于减少单个线程未竞争情况下时锁定和解锁的 CAS 操作。
一旦我们调用了对象的 hashcode 方法,将触发 identity hashcode 的计算(我们的对象没有重写 Object.hashcode),偏向锁被撤销为无锁,线程 ID 置换为 hashcode。
第二次执行到方法对象同步时,由于存放了 hashcode,这时检查到为不可偏向的无锁标志,虚拟机会将当前的 mark word 备份到当前线程方法的栈桢的 Lock Record 中,并通过 CAS 操作将 Lock Record 指针更新到对象的 mark word 中,如果 CAS 操作成功,那么该线程就获取了该对象上的锁,并且对象的 mark word 锁标记变为 00,表示该对象处于轻量级锁状态。在代码退出同步块时,轻量级锁释放,再通过 CAS 操作把对象当前的 mark word 和线程中复制的替换回来。
Java 是一门类型安全的语言,所以在很多地方都需要运行时类型检查。Class word 携带有关我们拥有的对象的实际类型的数据,这使编译器可以发出运行时类型检查。这些运行时检查的效率取决于元数据类型的形状。
如果元数据以简单的形式编码,则编译器甚至可以直接在代码流中内联那些检查。在 Hotspot 中,Class word 持有指向 VM Klass 实例的本机指针(Klass Pointer),该实例包含大量元信息,包括它继承的超类的类型,实现的接口等,它还带有 Java 镜像 - java.lang.Class 的关联实例。这种间接方式允许将 java.lang.Class 实例视为常规对象,并在 GC 期间不更新每个 class word 的情况下移动它们:java.lang.Class 可以移动,而 Klass 始终保持在同一位置。
6.2 确定对象大小
确定对象大小采用相似的方法。与运行时类型检查无法静态地知道对象的类型相比,分配确实或多或少地精确地知道了分配对象的大小:它由使用的构造函数的类型,使用的数组初始化器等定义。因此,在这些情况下,不需要通过 class word 进行访问。
$ jdk8-64/bin/java -cp jol-samples.jar org.openjdk.jol.samples.JOLSample_25_ArrayAlignment # Running 64-bit HotSpot VM. # Using compressed oop with 3-bit shift. # Using compressed klass with 3-bit shift.
[J object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 04 (object header) 01000000 # Mark word 44 (object header) 00000000 # Mark word 84 (object header) d8 0c 0000 # Class word 124 (object header) 00000000 # Array length 160long [J.<elements> N/A Instance size: 16 bytes Space losses: 0 bytes internal + 0bytesexternal=0 bytes total ...
[B object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 04 (object header) 01000000 # Mark word 44 (object header) 00000000 # Mark word 84 (object header) 68070000 # Class word 124 (object header) 00000000 # Array length 160byte [B.<elements> N/A Instance size: 16 bytes Space losses: 0 bytes internal + 0bytesexternal=0 bytes total
[B object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 04 (object header) 01000000 # Mark word 44 (object header) 00000000 # Mark word 84 (object header) 68070000 # Class word 124 (object header) 01000000 # Array length 161byte [B.<elements> N/A 177 (loss due to the next object alignment) Instance size: 24 bytes Space losses: 0 bytes internal + 7bytesexternal=7 bytes total
[B object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 04 (object header) 01000000 # Mark word 44 (object header) 00000000 # Mark word 84 (object header) 68070000 # Class word 124 (object header) 02000000 # Array length 162byte [B.<elements> N/A 186 (loss due to the next object alignment) Instance size: 24 bytes Space losses: 0 bytes internal + 6bytesexternal=6 bytes total
[B object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 04 (object header) 01000000 # Mark word 44 (object header) 00000000 # Mark word 84 (object header) 68070000 # Class word 124 (object header) 03000000 # Array length 163byte [B.<elements> N/A 195 (loss due to the next object alignment) Instance size: 24 bytes Space losses: 0 bytes internal + 5bytesexternal=5 bytes total
...
[B object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 04 (object header) 01000000 # Mark word 44 (object header) 00000000 # Mark word 84 (object header) 68070000 # Class word 124 (object header) 08 000000 # Array length 168byte [B.<elements> N/A Instance size: 24 bytes Space losses: 0 bytes internal + 0bytesexternal=0 bytes total