偏向锁撤销必然触发stw,因其需在全局安全点暂停所有线程以原子修改对象头mark word和各线程栈帧中的锁记录;jdk15+默认禁用,因撤销开销远超轻量级锁自旋收益。

偏向锁撤销不是“慢了一点”,而是会触发全局安全点(Safepoint)并导致 Stop-The-World(STW),这是现代高并发 Java 应用中必须规避的硬伤。
撤销本质是一次微型 STW
撤销操作不能在任意时刻执行。JVM 必须等所有应用线程都到达安全点(如方法返回、循环边界、字节码指令间隙),才能统一暂停它们,检查栈帧是否仍持有该锁、更新对象头 Mark Word,并可能分配 Monitor。这个等待和处理过程本身就是一次 STW——哪怕只持续 0.3–0.8ms,在 QPS 上万的服务里也足以造成响应毛刺、SLA 超时或 GC 延迟抖动。
- 单个对象被争用,就可能拖住全部线程;影响范围不取决于你锁了哪个对象,而取决于它所属的 class
- 撤销后锁通常直接升级为重量级锁(涉及操作系统 mutex),而非轻量级锁,开销进一步放大
- 即使只是调用一次 Object.hashCode(),也会强制撤销偏向状态——这点极易被忽略
批量撤销让开销成倍放大
JVM 不是逐个处理撤销,而是以 class 为单位统计。同一 class 下对象被撤销次数达到阈值,就会触发批量动作:
- BiasedLockingBulkRebiasThreshold = 20:该 class 第 20 次撤销时,JVM 批量重偏向——只更新 epoch,避免下一轮再进安全点,但仍是 STW
- BiasedLockingBulkRevokeThreshold = 40:第 40 次后,对该 class 所有现存对象批量撤销,后续新建对象也禁用偏向锁
- 注意:synchronized(obj) 触发撤销,但只有 synchronized(obj.getClass()) 才真正计入该 class 的计数器
撤销成本远超轻量级锁的 CAS 总和
在多核高并发场景下,一次撤销的 STW 成本,往往高于几十次轻量级锁的自旋 CAS 开销。原因在于:
- 现代 CPU 上 CAS 是极快的原子指令,而 STW 是全局调度行为,涉及线程挂起、栈遍历、内存屏障等重型操作
- 轻量级锁配合自适应自旋(adaptive spinning),能有效应对短暂竞争;偏向锁却在第一次竞争就“缴枪”,还拉全量线程陪跑
- 短命对象若逃逸分析成功,JIT 可直接锁消除——此时开启偏向锁反而干扰优化路径
如何确认你的系统正被撤销拖累
别靠猜测。用 JVM 自带工具看真实指标:
- 启动参数加 -XX:+PrintBiasedLockingStatistics -XX:+UnlockDiagnosticVMOptions
- 运行后执行 jcmd
VM.native_memory summary 或观察 jstat -compiler是否出现编译停滞 - 最直接:用 jstack
抓快照,搜索 “biased lock revocation” 或大量线程卡在 ObjectSynchronizer::slow_enter
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











