偏向锁的mark word低3位为101,轻量级锁为000,重量级锁为010;三者通过对象头标记位识别,状态迁移需结合字节码、jol输出与jvm参数验证。

偏向锁的 Mark Word 位模式怎么识别
偏向锁不是靠操作系统互斥量,而是靠对象头 Mark Word 中的线程 ID 和 epoch 字段做标记。JVM 在对象未被锁时,Mark Word 的低 3 位为 001(表示无锁),一旦启用偏向锁且首次被某线程获取,JVM 会用 CAS 将线程 ID 写入 Mark Word 高位,并把低 3 位设为 101 —— 这就是偏向锁的固定标识。
常见误判点:
- 直接读取
Mark Word十六进制值却忽略 JVM 大端/小端布局,导致位解析错位 - 在开启
-XX:-UseBiasedLocking时仍试图观察偏向锁行为,实际已全局禁用 - 使用 JOL(Java Object Layout)工具时未加
-XX:BiasedLockingStartupDelay=0,导致前 4 秒内对象默认不进入偏向状态
轻量级锁触发时 Mark Word 怎么变化
当另一个线程尝试获取已被偏向的锁,JVM 先撤销偏向锁(revoke),再尝试用 CAS 将当前线程栈帧中的锁记录地址写入 Mark Word,同时把低 3 位改为 000 —— 这是轻量级锁的标志位。此时 Mark Word 不再存线程 ID,而是一个指向栈中 Lock Record 的指针。
关键细节:
- CAS 失败不等于立即升级:失败后线程会先自旋(默认 10 次,可调
-XX:PreBlockSpin),自旋期间Mark Word保持000状态 - 自旋中若持有锁线程退出同步块,
Mark Word会被恢复为无锁状态(001),其他线程可能重新抢到偏向锁 - 自旋耗尽仍未获取成功,才触发锁膨胀,将
Mark Word改为指向ObjectMonitor的指针,低 3 位设为010
重量级锁的 010 标识为什么不可逆
Mark Word 低 3 位为 010 表示重量级锁,此时对象头存储的是指向 ObjectMonitor 结构的指针。这个结构由 JVM 在堆上分配,关联操作系统的 mutex,因此涉及用户态/内核态切换。
锁不会降级的根本原因:
-
ObjectMonitor一旦创建,其内部队列(_EntryList、_WaitSet)可能已有阻塞线程,降级会破坏等待语义 - 偏向锁和轻量级锁依赖线程栈帧生命周期,而重量级锁已脱离栈帧管理,无法安全回退
- JVM 源码中
inflate函数只做单向构造,没有对应的deflate实现逻辑
怎么验证锁状态迁移过程
不能只靠日志或猜测,必须结合字节码 + 对象头 + JVM 参数三者交叉验证:
- 用
javap -v确认同步块是否生成monitorenter/monitorexit指令 - 用 JOL 的
ClassLayout.parseInstance(obj).toPrintable()输出实时Mark Word十六进制值,并对照 JVM 源码中markOop.hpp的位定义表解码 - 加上
-XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1观察偏向锁撤销是否引发 safepoint - 避免在容器类(如
HashMap)或字符串常量池对象上测试 —— 它们默认禁用偏向锁
真正容易被忽略的是:锁膨胀不是发生在“加锁瞬间”,而是在竞争发生后的**锁获取尝试失败时**才触发;且所有状态变更都以对象为粒度,同一个类的不同实例可能处于完全不同的锁状态。










