直接通过日志中“biased lock revocation”和“deflate”频次与时间密度可判断锁竞争是否超安全水位:高频“revoked bias”(>5–10次/秒)、“biased lock revocation counter”突增、及“inflate/deflate”交替震荡,结合jstack线程阻塞与jmap对象分布分析,可精准识别真实高争用;需排除启动初期、单例初始化、gc触发等假性信号;按60秒窗口分级:≤5次为绿,6–50次为黄,≥51次且含≥3批次批量撤销并伴≥15线程同锁阻塞为红。

直接看日志里“biased lock revocation”和“deflation”出现的频次与时间密度,就能判断锁竞争是否已越过安全水位。真正激烈的锁竞争,不是靠线程数或CPU使用率来体现的,而是反映在JVM对锁状态的频繁干预上——尤其是偏向锁被大量、集中地撤销。
重点关注三类日志信号
这些日志通常出现在 GC 日志或启用 -XX:+PrintBiasedLockingStatistics 后的标准输出中,不是应用层 log,需从 JVM 运行时采集:
- “Revoked bias of object”高频出现:每秒超过 5–10 次,说明多个线程正反复争抢同一对象锁,偏向锁已完全失效;
- “Biased lock revocation counter”突增:该计数器在短时间(如 1 秒内)跳升几十甚至上百,表明发生了批量撤销(bulk revocation),是竞争升级的关键标志;
- 伴随大量“inflate”和“deflate”交替记录:例如同一对象在几毫秒内先 inflate 成轻量级锁,又被 deflate 回无锁,再 inflate ——这是锁状态在“无锁→偏向→轻量→重量”间高频震荡,典型高争用特征。
结合线程栈与锁对象分布交叉验证
单看撤销日志只能知“有竞争”,要判“多激烈”,必须关联分析:
- 用
jstack -l <pid></pid>抓取线程快照,搜索parking to wait for或waiting to lock,统计阻塞在线程数 > 20 且集中在同一对象监视器(如0x0000000800a1b2c0)的线程批次; - 用
jmap -histo:live <pid></pid>查看高频被加锁的对象类型(如ConcurrentHashMap$Node、String缓存键等),若某类对象实例数少但撤销次数极高,说明热点集中、争用尖锐; - 观察撤销发生时段是否与业务峰值(如整点秒杀、定时任务触发)强重合——时间相关性越强,越说明是真实业务驱动的竞争,而非偶发抖动。
区分“假性高水位”与真实风险
有些撤销日志看似密集,实则无害,需过滤干扰:
- 应用启动初期大量撤销属于正常:JVM 默认开启偏向锁,但新创建对象尚未被任何线程偏向,首次竞争即触发撤销,此时不反映运行时压力;
- 仅个别对象(如静态配置单例)被撤销几次,但无后续重复,属低频初始化行为,无需干预;
- 撤销日志与
Full GC时间点高度重叠,大概率是 GC 触发的批量去偏向(safepoint action),应查 GC 日志确认,而非归因为并发竞争。
水位分级参考(以 60 秒窗口为单位)
按生产环境可观测性经验设定阈值:
- 绿色(安全):撤销次数 ≤ 5 次,无批量迹象,无线程堆积;
- 黄色(关注):撤销 6–50 次,出现 1–2 批次 bulk revocation,有少量线程 BLOCKED;
-
红色(高危):撤销 ≥ 51 次,含 ≥ 3 批次 bulk revocation,且
jstack显示 ≥ 15 线程等待同一锁地址,或平均锁膨胀延迟 > 10μs(可通过 JFR 事件验证)。










