导读:本期聚焦于辉辉创作的《如何分析 JVM 的 Biased Locking 偏向锁撤销对高竞争系统产生的性能抖动》,敬请观看详情。高并发服务突然出现周期性响应变慢,线程栈里大量停在安全点,往往和偏向锁批量撤销有关。偏向锁在单线程访问时免去了CAS开销,但一旦有其它线程尝试竞争,JVM就要撤销偏向状态并可能触发stop-the-world。本文从底层机制讲清撤销如何引发安全点停顿,再用JFR和汇编日志定位撤销热点,最后给出关闭偏向或改用轻量级锁的调优思路,帮助你在高竞争场景下消除这种隐蔽的性能抖动。

在多线程高竞争的服务端应用中,JVM默认开启的偏向锁本意是优化无竞争场景下的加锁开销,但当大量线程频繁访问同一监视器时,偏向锁的撤销机制反而会成为性能抖动的源头。这种抖动通常表现为每隔数秒出现一次几十毫秒甚至上百毫秒的停顿,监控曲线呈锯齿状,而普通CPU利用率指标却看不出异常。理解偏向锁撤销在JVM内部的执行路径,是分析此类问题的第一步。

如何分析 JVM 的 Biased Locking 偏向锁撤销对高竞争系统产生的性能抖动

偏向锁撤销的底层原理与安全点停顿

偏向锁的核心思想是:如果一个监视器对象被某个线程首次获取,JVM会在对象头中记录该线程ID,此后该线程再次进入同步块时只需比对线程ID而无需原子指令。但当另一个线程尝试获取已被偏向的锁时,JVM必须执行偏向撤销。撤销操作并非简单修改标记,它要求持有偏向锁的线程到达安全点,由虚拟机暂停所有相关线程后,再决定是否将锁升级为轻量级锁或重量级锁。

在HotSpot中,偏向锁撤销分为撤销和重偏向两类。单次撤销只影响一个对象,但JVM引入了批量撤销与批量重偏向的阈值机制。当某个类的对象发生偏向撤销次数达到阈值,例如二十次,该类后续新对象将直接禁用偏向;若达到另一阈值则触发批量重偏向。问题在于,这些批量操作都需要在全局安全点执行,高竞争下类级别的批量撤销会瞬间挂起所有线程,造成明显的STW停顿。

我们可以通过对象头布局理解这一过程。开启偏向锁后,对象头Mark Word包含是否偏向、锁标志位与线程指针。撤销时,JVM要遍历当前持有线程的栈帧找到锁记录,并清理相关偏向信息。如果此时系统正处于高竞争,每秒成千上万次错误偏向叠加,撤销线程与安全点协调的成本会远大于轻量级锁本身的CAS开销,此时偏向锁已经从优化变成了负担。

使用JFR与GC日志定位撤销热点

要确认性能抖动是否由偏向锁撤销引起,最直接的方式是开启Java Flight Recorder。JFR中提供了jdk.BiasedLockRevocationjdk.BiasedLockClassRevocation事件,能够记录每次撤销发生的类、线程与持续时间。在JMC中过滤这些事件,若发现同一类频繁出现且伴随安全点事件jdk.Safepoint的长时间暂停,即可锁定问题根源。

除了JFR,还可以在启动参数中加入-XX:+PrintBiasedLockingStatistics-XX:+TraceBiasedLocking。前者在JVM退出时打印各类的偏向与撤销计数,后者则在运行时输出撤销决策细节。下面是一段用于开启诊断参数的示例:

java -XX:+UseBiasedLocking 
     -XX:+PrintBiasedLockingStatistics 
     -XX:+TraceBiasedLocking 
     -XX:+UnlockDiagnosticVMOptions 
     -jar high-contention-service.jar

对于已上线且不能频繁重启的系统,可以借助jstack周期性采样,观察线程是否大量阻塞在ObjectMonitor::enter或安全点轮询。如果发现很多线程栈顶显示偏向锁相关内部方法,再结合监控中的停顿时间点,就能建立撤销与抖动的因果关联。需要注意的是,安全点停顿本身可能由其他原因引起,必须交叉比对JFR中的偏向锁事件时间戳。

高竞争场景下的调优与替代方案

一旦确认偏向锁撤销是抖动主因,最简单的做法是关闭偏向锁。从JDK 15开始偏向锁已被标记为废弃,而在早期版本中可使用-XX:-UseBiasedLocking直接禁用。关闭后,所有锁从一开始走轻量级锁路径,虽在无竞争时多了一次CAS,但彻底消除了撤销带来的安全点风险。对于高竞争系统,这通常是正向收益。

如果业务必须使用旧JDK且不能全局关闭,可通过-XX:BiasedLockingStartupDelay=0配合类级别的排除。例如使用-XX:CompileCommand=exclude或白名单机制减少特定热点类的偏向。但从工程角度看,高竞争下更合理的结构是减少共享监视器:用并发容器替代同步块、将锁拆分为分段锁、或采用无锁算法。下面示例展示用ReentrantLock配合tryLock降低阻塞:

import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private final ReentrantLock lock = new ReentrantLock();
    private long value = 0;

    public boolean add(long delta) {
        if (lock.tryLock()) {
            try {
                value += delta;
                return true;
            } finally {
                lock.unlock();
            }
        }
        return false;
    }
}

最后要强调的是,偏向锁撤销的性能抖动具有隐蔽性,它不会抛异常也不会显著增加CPU占用,仅靠APM的响应时间百分位就能察觉。建议高竞争系统在压测阶段就开启JFR做基线采集,对比开启与关闭偏向锁的P99延迟。当看到批量撤销事件消失且长尾延迟下降,便说明调优生效。架构层面则应持续审视同步粒度,让JVM的锁机制运行在它擅长的低竞争区间。

Biased_Locking偏向锁撤销JVM性能分析修改时间:2026-08-18 06:14:28

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。