最直接有效的方式是加启动参数 -XX:-UseBiasedLocking,JDK 15+ 默认已关闭,但 JDK 8–14 仍默认开启,高并发下因频繁锁撤销导致 STW 和性能下降。

手动禁用 JVM 偏向锁,最直接有效的方式是加启动参数 -XX:-UseBiasedLocking,JDK 15+ 默认已关闭,但 JDK 8–14 仍默认开启,高并发场景下不关它,反而会因频繁锁撤销拖慢性能。
为什么高并发下偏向锁反而成负优化
偏向锁本意是为「无竞争」场景省掉 CAS 开销,但一旦出现多线程争用,JVM 就得执行锁撤销(revoke)——这过程要 stop-the-world、遍历所有线程栈、检查锁记录,开销远超普通轻量级锁。尤其在 Web 容器、RPC 服务等短生命周期对象频繁加锁的场景,biased lock revocation 可能占到同步操作总耗时的 20% 以上。
- 典型错误现象:
PrintGCApplicationStoppedTime显示大量毫秒级停顿,且日志里反复出现Bias Revocation相关 trace - 不是所有锁都会触发撤销:只有已偏向某线程、又被其他线程尝试获取的锁才撤销;新创建对象的锁不会立刻偏向(有延迟,默认 4s)
- JDK 10 后引入了
-XX:BiasedLockingStartupDelay=0强制立即启用偏向锁,但对高并发服务毫无意义,反而加速问题暴露
-XX:-UseBiasedLocking 的实际生效条件
这个参数必须在 JVM 启动时指定,运行中无法动态关闭;而且它只影响新创建的对象,已偏向的对象不会自动“去偏向”,得等下次 GC 或显式触发 safepoint 才可能清理。
- 必须放在
java命令最前面或紧跟-jar之前,写在-D参数后面可能被忽略 - 和
-XX:+UseParallelGC等 GC 参数无冲突,但和-XX:+UnlockExperimentalVMOptions无关(它不属实验性选项) - 某些容器(如 Spring Boot fat jar)会把 JVM 参数塞进
java -Dloader.path=... -jar,此时需确保-XX:-UseBiasedLocking在-jar左侧
验证是否真正禁用成功
光看参数没用,得确认运行时行为。最可靠方式是开启 JVM 锁诊断日志,而不是依赖 jstat 或 JMX 中模糊的统计值。
- 加参数
-XX:+PrintBiasedLockingStatistics -XX:+UnlockDiagnosticVMOptions,启动后搜日志里的biased locking is disabled - 若看到
Total skipped revocations: 0且后续无Revoked bias行,基本可确认已禁用 - 注意:JDK 15+ 默认输出里会直接写
Biased locking is disabled,但 JDK 8u292 之前需手动开诊断选项才能看到明确提示
真正麻烦的是那些混合部署环境——同一台机器跑着 JDK 8 的老服务和 JDK 17 的新服务,参数习惯不统一,配置中心下发时容易漏掉旧版本的 -XX:-UseBiasedLocking。这种地方,比写错一个函数名更难排查。










