Mark Word 深度解析:对象头的“密码本”

一、Mark Word 的内存布局

Mark Word 在 64 位 JVM 中占 8 字节(64 bits),其内容根据对象状态动态变化,具体分为以下 5 种模式:

锁状态 存储内容 位分布(64 bits)
无锁 哈希码(31 bits) + 分代年龄(4 bits) + 偏向模式(1 bit) + 锁标志位(2 bits) `unused:25
偏向锁 线程ID(54 bits) + 时间戳(epoch,2 bits) + 分代年龄(4 bits) + 锁标志位 `thread:54
轻量级锁 指向栈中锁记录(Lock Record)的指针(62 bits,高位为 0) + 锁标志位 `ptr_to_lock_record:62
重量级锁 指向 Monitor 对象(ObjectMonitor)的指针(62 bits,高位为 0) + 锁标志位 `ptr_to_monitor:62
GC 标记 用于垃圾回收的标记信息(如对象存活状态) `未使用:62

关键字段解释

  • 锁标志位(2 bits):标识当前锁状态(01=无锁/偏向锁,00=轻量级锁,10=重量级锁,11=GC标记)。
  • 偏向模式(1 bit):仅当锁标志位为 01 时有效,1 表示偏向锁,0 表示无锁。
  • 分代年龄(4 bits):记录对象经历过的 GC 次数(最大值 15,触发后晋升老年代)。
  • 哈希码(31 bits) :延迟计算,首次调用 hashCode() 时生成。
  • 线程ID(54 bits):偏向锁持有者的线程 ID(实际是线程指针的哈希值)。

二、Mark Word 的动态变化

通过一个简单示例观察 Mark Word 的变化(使用 JOL 工具):

java 复制代码
// 添加依赖:org.openjdk.jol:jol-core
Object obj = new Object();
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
1. 初始状态(无锁)

输出结果:

css 复制代码
OFFSET  SIZE   TYPE DESCRIPTION            
      0     4        (object header)      01 00 00 00  # Mark Word(01 表示无锁)
      4     4        (object header)      00 00 00 00  # 类型指针(压缩后)
      8     4        (object header)      e5 01 00 00  # 数组长度(非数组对象为 0)
     12     4        (loss due to the next object alignment)
  • 此时锁标志位为 01,偏向模式为 0(无锁)。

2. 偏向锁状态

当线程首次获取偏向锁时,Mark Word 会记录线程 ID 和 epoch:

java 复制代码
synchronized (obj) {
    System.out.println(ClassLayout.parseInstance(obj).toPrintable());
}

输出结果:

css 复制代码
OFFSET  SIZE   TYPE DESCRIPTION            
      0     4        (object header)      05 e0 00 20  # Mark Word(偏向锁,线程ID、epoch)
      4     4        (object header)      00 00 00 00  
      8     4        (object header)      e5 01 00 00  
     12     4        (loss due to the next object alignment)
  • 锁标志位仍为 01,但偏向模式位变为 1
  • 线程ID 和 epoch 被写入高位。

3. 轻量级锁状态

当第二个线程尝试获取锁时,偏向锁撤销,升级为轻量级锁:

java 复制代码
Thread t1 = new Thread(() -> {
    synchronized (obj) { /* ... */ }
});
Thread t2 = new Thread(() -> {
    synchronized (obj) { /* ... */ }
});
t1.start(); t1.join(); // 触发偏向锁
t2.start(); t2.join(); // 触发轻量级锁
System.out.println(ClassLayout.parseInstance(obj).toPrintable());

输出结果:

css 复制代码
OFFSET  SIZE   TYPE DESCRIPTION            
      0     4        (object header)      f0 f8 7f 02  # 指向栈中锁记录的指针(00 结尾)
      4     4        (object header)      00 00 00 00  
      8     4        (object header)      e5 01 00 00  
     12     4        (loss due to the next object alignment)
  • 锁标志位变为 00,表示轻量级锁。
  • Mark Word 存储指向线程栈中锁记录的指针。

4. 重量级锁状态

当竞争激烈时,升级为重量级锁:

java 复制代码
for (int i = 0; i < 100; i++) {
    new Thread(() -> {
        synchronized (obj) { /* 模拟高竞争 */ }
    }).start();
}
System.out.println(ClassLayout.parseInstance(obj).toPrintable());

输出结果:

css 复制代码
OFFSET  SIZE   TYPE DESCRIPTION            
      0     4        (object header)      5a 3d 01 00  # 指向 ObjectMonitor 的指针(10 结尾)
      4     4        (object header)      00 00 00 00  
      8     4        (object header)      e5 01 00 00  
     12     4        (loss due to the next object alignment)
  • 锁标志位变为 10,表示重量级锁。
  • Mark Word 存储指向 ObjectMonitor 的指针。

三、关键设计细节

  1. 哈希码的延迟计算

    • 对象创建时不会立即计算哈希码,首次调用 hashCode() 时生成并写入 Mark Word。
    • 若对象已处于偏向锁或轻量级锁状态,哈希码会丢失(需撤销锁状态)。
  2. 偏向锁的 epoch 机制

    • epoch 用于批量撤销偏向锁。当某一类对象的偏向锁被频繁撤销时,JVM 会递增 epoch,使旧的偏向锁失效,避免全局撤销。
  3. 指针压缩优化

    • 在 64 位 JVM 开启压缩指针(默认开启)时,类型指针仅占 4 字节,Mark Word 仍为 8 字节。

四、实际应用中的问题

  1. 哈希码与锁的冲突

    • 若对象已计算哈希码,则无法进入偏向锁状态(哈希码占用 Mark Word 空间)。
    • 代码中过早调用 hashCode() 可能导致偏向锁失效。
  2. 偏向锁的性能陷阱

    • 高竞争场景下,偏向锁的撤销和升级会增加开销(JDK 15 后默认禁用偏向锁)。
  3. 调试技巧

    • 使用 jol-core 分析对象头,快速定位锁状态问题。
    • 通过 -XX:+PrintBiasedLockingStatistics 参数打印偏向锁统计信息。

总结

Mark Word 是 JVM 同步机制的基石,其动态位模式设计实现了锁状态的灵活切换。理解 Mark Word 的细节,能够:

  1. 优化高并发代码(如避免哈希码与偏向锁冲突)。
  2. 诊断多线程问题(如通过对象头分析锁竞争)。
  3. 掌握 JVM 调优技巧(如偏向锁延迟参数的调整)。

扩展工具

相关推荐
Wyc724097 分钟前
SpringBoot
java·spring boot·spring
Bella_chene9 分钟前
IDEA中无法使用JSP内置对象
java·servlet·intellij-idea·jsp
凯基迪科技1 小时前
exe软件壳的分类----加密保护壳
java
wuxuanok1 小时前
Web后端开发-分层解耦
java·笔记·后端·学习
kyle~2 小时前
C/C++字面量
java·c语言·c++
neoooo2 小时前
别慌,Java只有值传递——一次搞懂“为啥我改了它还不变”!
java·后端·spring
秋难降2 小时前
Python 知识 “八股”:给有 C 和 Java 基础的你😁😁😁
java·python·c
wuxuanok2 小时前
Web后端开发-请求响应
java·开发语言·笔记·学习
livemetee2 小时前
spring-ai 1.0.0 (3)交互增强:Advisor 顾问模块
java
DDDDDouble2 小时前
<二>Sping-AI alibaba 入门-记忆聊天及持久化
java·人工智能