markword 是 hotspot 对象头中复用存储锁状态、哈希码、gc年龄等元信息的动态字段,其64位布局随对象状态(无锁/偏向/轻量级/重量级/ gc标记)变化而动态解释。

MarkWord 是 HotSpot 虚拟机中对象头(Object Header)的核心组成部分,它不存储用户数据,却承载着对象的运行时元信息——锁状态、哈希码、GC 分代年龄、偏向线程 ID 等。理解它的内存布局,是深入 JVM 锁优化、垃圾回收和对象生命周期管理的关键一步。
MarkWord 的作用与设计动机
HotSpot 为节省内存并支持多种运行时语义,在对象头中用一个固定大小的字段(通常是 64 位或 32 位,取决于是否开启指针压缩)复用多个含义。这个字段就是 MarkWord。它不是“一值一意”,而是根据对象当前状态动态解释其二进制位。
例如:对象刚创建时,MarkWord 可能保存 identity hash code;被加轻量级锁后,它变成指向栈中锁记录的指针;进入偏向模式后,又存偏向线程 ID 和 epoch;被 GC 标记时,还可能被复用为 forwarding pointer 或 GC 相关标记位。
64 位虚拟机下的典型 MarkWord 布局(开启指针压缩)
在主流服务端环境(64 位 JVM + UseCompressedOops),对象头结构如下(以普通对象、未锁、未偏向为例):
- 前 25 位:identity_hashcode(未计算时为 0,首次调用 System.identityHashCode() 后写入)
- 接下来 1 位:age(GC 年龄,最大 15,实际只用低 4 位)
- 接下来 2 位:biased_lock(偏向锁标志,01 表示启用偏向,00 表示未启用)
- 接下来 1 位:lock(锁状态标志,01=轻量级锁,10=重量级锁,11=GC 标记,00=无锁/偏向中)
- 剩余 36 位:根据 lock 和 biased_lock 组合决定含义——可能是偏向线程 ID、栈中锁记录地址、monitor 地址,或为空闲位
注意:具体位宽分配依赖 JVM 版本(如 JDK 8u 与 JDK 17 差异细微)、是否开启 UseBiasedLocking、是否使用 ZGC/Shenandoah 等。可通过 -XX:+PrintJNISupportedTypes 或分析 OpenJDK 源码中的 markOop.hpp 获取确切定义。
如何验证和观察 MarkWord 内容
直接读取对象头需绕过 Java 安全限制,常用方式有:
- 使用 Unsafe.objectFieldOffset 配合 Unsafe.getLong(需反射获取 Unsafe 实例)读取对象内存起始偏移处的 8 字节
- 借助 JOL(Java Object Layout) 工具:
new ClassLayout.parseInstance(obj).toPrintable()可输出含 MarkWord 十六进制值及语义解析的完整布局 - 开启 JVM 参数 -XX:+PrintGCDetails -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly(需 hsdis),结合调试器观察锁膨胀过程中的 MarkWord 变化
例如:执行 synchronized 块前后用 JOL 打印同一对象,可清晰看到 lock 位从 00 → 01 → 10 的跃迁,以及对应字段内容的切换。
常见误区与注意事项
MarkWord 不是“静态结构”,它的解读高度依赖上下文:
- 调用 System.identityHashCode() 不一定立即写入 MarkWord —— 若对象已加锁,hashcode 会暂存于 monitor 中,解锁后才回填(或丢弃)
- 偏向锁撤销时,MarkWord 不是简单“清零”,而是先升级为轻量级锁,再根据竞争情况决定是否膨胀;该过程涉及 CAS 修改和多线程协作
- GC 过程中(尤其 CMS、ZGC 的并发标记阶段),MarkWord 可能被临时覆盖为 mark bit 或 forwarding address,此时常规解读会失效
- 数组对象的 MarkWord 布局与普通对象一致,但对象头后紧跟的是数组长度(4 字节),而非类指针(Klass Pointer)
不复杂但容易忽略:MarkWord 的每一次位变化,背后都是 JVM 运行时对性能与语义的精细权衡。










