Java锁机制的底层本质是对象头中8字节Mark Word的动态位域复用,它不占独立字段空间,而是随锁状态(无锁/偏向/轻量级/重量级/GC标记)实时切换二进制位解释方式,通过最低2–3位锁标志位结合高位交叉验证判断状态,需借助JOL或JHSDB等工具解析,而非反射或Unsafe。

Java 锁机制的底层本质,就藏在对象头的 Mark Word 这 8 字节里——它不是字段,而是一块动态复用的位域内存,锁状态、哈希码、GC 年龄等信息全靠它实时切换解释方式。
Mark Word 不是字段,是运行时位域
它没有 Java 层变量名,不占独立字段空间,无法用 Unsafe.objectFieldOffset() 查偏移;它是对象头中固定 8 字节(64 位 JVM + 压缩指针下)的物理内存区域,内容随对象状态实时覆盖:
- 刚创建未加锁:可能存 identity hash code(25 位)或 GC 年龄(4 位)
- 启用偏向锁后:低 2 位为 01,后面紧接线程 ID、epoch 和时间戳
- 轻量级锁时:高 62 位存指向当前线程栈中
Lock Record的指针 - 重量级锁时:存指向堆中
ObjectMonitor的指针 - GC 标记阶段:可能复用为 forwarding pointer 或 mark bit(如 CMS 中的 11)
锁标志位在哪?怎么判断当前锁状态
锁状态由 Mark Word 最低 2–3 位决定,但必须结合高位交叉验证,不能只看低位:
- 01:表示无锁态或偏向锁——需检查第 3 位(biased_lock)是否为 1,且后续有非零线程 ID 才是真偏向锁
- 00:轻量级锁(此时高 62 位是栈中 Lock Record 地址)
- 10:重量级锁(高 62 位是 ObjectMonitor* 指针)
- 11:GC 相关标记(如 CMS mark-sweep 阶段)
注意:JDK 8u 后默认开启偏向锁,但 JDK 15+ 已默认禁用;实际判断必须依赖 BiasedLocking::enabled() 运行时开关,硬编码位掩码(如 mark & 0x3 == 0x1)不可靠。
立即学习“Java免费学习笔记(深入)”;
怎么真实看到 Mark Word 的值和含义
不能靠反射或 Unsafe 直接“读字段”,得用专用工具解析运行时内存布局:
- 用 JOL:执行
java -jar jol-cli.jar internals java.lang.Object,输出中的mark:行就是当前十六进制值 - 代码中调用:
ClassLayout.parseInstance(obj).toPrintable(),可打印含语义注释的完整布局 - 配合 JHSDB 或 hsdis 反汇编 hotspot 源码
markOop.hpp,对照位定义手动解码(如 JDK 17 中偏向锁 epoch 占 2 位,线程 ID 占 54 位)
例如一个刚 new 出的对象,JOL 显示 mark: 0x0000000000000001,低两位 01 + 全零线程 ID → 表示无锁态,而非偏向锁。
为什么对象地址不变,Mark Word 却总在变
因为 Mark Word 是对象头的一部分,而对象头与实例数据连续存储在堆中同一块内存页上。加锁、升级、GC 等操作只是修改这 8 字节的二进制位解释逻辑,对象本身地址没挪动:
- synchronized 块进入时,JVM 修改 Mark Word 为轻量级锁格式(写入栈指针)
- 发生竞争后膨胀为重量级锁,Mark Word 被覆写为 monitor 地址
- GC 时 CMS 或 ZGC 可能复用部分位做标记,不影响对象存活判定逻辑
这种设计节省内存、避免额外字段开销,也契合 CPU 缓存行友好原则——所有元信息压缩在单个 cache line 内完成原子更新。


















