导读:本期聚焦于永濑创作的《锁优化之偏向锁撤销:解析 Safepoint 对所有变量线程执行同步的影响》,敬请观看详情。为什么一次看似普通的偏向锁撤销,会引发整个 JVM 进程的短暂停顿?这背后涉及 HotSpot 虚拟机中 Safepoint 机制与批量偏向撤销之间的交互。当某个线程尝试获取已被偏向的锁而偏向身份不匹配时,JVM 需要执行撤销操作,而该操作必须在 Safepoint 处以 VM Operation 的形式运行,这就要求虚拟机让所有线程到达安全点,代价相当于一次全局停顿。本文从偏向锁的内存布局讲起,逐步分析撤销流程、Safepoint 的同步原理,并结合 JVM 参数与日志观察批量撤销与批量重偏向的触发条件,最后给出在高并发场景下是否应关闭偏向锁的实践建议,帮助开发者理解锁优化的隐藏成本。

偏向锁曾是 HotSpot 虚拟机引以为傲的一项轻量级锁优化,其核心假设是:一把锁在大多数生命周期里只会被同一个线程持有。在这样的前提下,JVM 只需在对象头里记录线程 ID,后续该线程进入同步块时无需任何原子操作即可通过判断。然而这项优化有一个容易被忽视的代价,当偏向关系需要被打破时,撤销操作必须借助 Safepoint 完成,也就是说,一次小小的锁撤销可能要求虚拟机暂停所有正在运行的线程,让它们全部到达安全点之后再继续。理解这个机制,对排查高并发系统中的偶发性停顿问题非常关键。

锁优化之偏向锁撤销:解析 Safepoint 对所有变量线程执行同步的影响

偏向锁的底层结构与撤销的由来

在 HotSpot 的对象内存布局中,对象头(Mark Word)承担了记录锁状态的任务。在 64 位 JVM 上,处于偏向模式的对象头会包含偏向线程的 ID、一个 epoch 值以及锁标志位。当持有偏向的线程再次进入同步块时,只需比对对象头中的线程 ID 是否等于自己,匹配则直接通过,整个过程没有任何 CAS 操作,这就是偏向锁快的原因。

问题出现在出现竞争的时刻。假设线程 A 持有偏向,而线程 B 尝试获取同一把锁,JVM 发现对象头的偏向线程不是 B,就必须执行偏向撤销,把对象头的锁状态退回为无锁或轻量级锁。撤销并不是简单地改几个比特位,因为偏向线程 A 可能正在同步块内执行,JVM 需要遍历 A 的栈帧,找到关联该锁的锁记录,判断锁是否仍被持有,再决定升级为轻量级锁还是恢复为无锁状态。遍历线程栈这种操作可能改变栈上的数据结构,只有在所有线程都停在稳定状态时才是安全的,这正是撤销必须依赖 Safepoint 的根本原因。

// 触发偏向撤销的典型场景
public class BiasRevokeDemo {
    static final Object lock = new Object();

    public static void main(String[] args) throws Exception {
        synchronized (lock) {
            // 主线程第一次获取,lock 被偏向给主线程
            System.out.println("biased to main thread");
        }
        Thread t = new Thread(() -> {
            // 另一个线程尝试获取,触发偏向撤销
            // 撤销需要 VM_Thread 执行,等待 Safepoint
            synchronized (lock) {
                System.out.println("acquired by worker");
            }
        });
        t.start();
        t.join();
    }
}

上述代码中,工作线程进入同步块的那一刻,JVM 会向全局提交一个撤销请求。这个请求由 VM Operation 机制处理,本质上是把一个任务塞进虚拟机的操作队列,然后等待所有线程到达安全点后统一执行。

Safepoint 的全局同步机制与停顿成本

Safepoint 是 JVM 中一个全局的同步点,处于该点的线程状态对虚拟机而言是完全已知且可安全操作的。触发 Safepoint 时,虚拟机会设置一个轮询标志,各个线程在解释执行的字节码边界、JIT 编译代码中的回边和返回点、以及执行本地方法返回时检查这个标志,一旦发现需要进入安全点,就会主动挂起自己。当最后一个线程也到达安全点后,VM Operation 才开始执行。

关键问题在于:Safepoint 的耗时取决于最慢到达的那个线程。如果某个线程正在执行一个没有安全点检查的长循环,或者正在执行一段耗时的本地代码,其他所有线程即使早已就绪也必须等待它。对于偏向锁撤销而言,撤销本身的逻辑很快,但从提交请求到所有线程就绪这段时间,整个应用是全局停顿的。在低延迟交易系统或高频调用路径上,这种停顿可能表现为微秒到毫秒级的尾延迟抖动,且常规的锁竞争监控手段难以直接观测到。

更麻烦的是撤销存在放大效应。JVM 定义了两个阈值来应对频繁撤销:当撤销次数超过 BiasedLockingBulkRebiasThreshold(默认 20)时触发批量重偏向,通过递增类信息中的 epoch 值让旧偏向批量失效;当撤销次数继续超过 BiasedLockingBulkRevokeThreshold(默认 40)时,整个类的新对象都不再允许偏向。即便如此,批量重偏向和批量撤销本身也是 VM Operation,同样要经过 Safepoint,所以在锁竞争激烈的场景下,偏向锁反而成了额外的负担来源。

如何观察与验证偏向撤销带来的停顿

要确认系统中是否存在偏向撤销引发的 Safepoint 停顿,可以打开相关 JVM 日志。以 JDK 8 为例,参数 -XX:+PrintSafepointStatistics 配合 -XX:+PrintGCApplicationStoppedTime 能输出每次 Safepoint 的类型和耗时,其中操作类型为 RevokeBiasBulkRevokeBias 的记录就是偏向撤销的直接证据。

# JDK 8 观察偏向撤销相关的 Safepoint 统计
java -XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0 \
     -XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1 \
     -XX:+PrintGCApplicationStoppedTime \
     -jar app.jar

# 输出示例中关注这些字段:
# RevokeBias    - 普通偏向撤销
# BulkRevokeBias - 批量撤销
# total_time_ns 表示该次安全点操作的同步与执行耗时

另外,偏向锁默认有 4 秒的启动延迟(BiasedLockingStartupDelay=4000),这是为了避免应用启动阶段大量锁尚未稳定时就发生撤销。若日志中 RevokeBias 频繁出现且 sync 时间偏高,基本可以判定偏向锁在当前负载下得不偿失。

实践建议:什么情况下应该关闭偏向锁

当应用呈现明确的多线程竞争模式,例如线程池中的任务共享缓存对象、消息消费者并发竞争同一把锁时,偏向锁几乎一定会经历撤销、重偏向再到轻量级锁升级的完整流程,每次撤销都附带 Safepoint 成本。这种场景下可以显式加上 -XX:-UseBiasedLocking 直接关闭偏向优化,让锁直接走轻量级到重量级的路径。

值得注意的是,JDK 15 之后偏向锁已被默认废弃,并在后续版本中彻底移除,官方给出的理由正是撤销操作需要 Safepoint、维护成本高且在现代无竞争同步已经足够快的背景下收益有限。如果你的系统仍运行在 JDK 8 或 11 上,又对尾延迟敏感,关闭偏向锁往往能消除一类隐蔽的停顿来源。如果锁确实长期单线程持有,例如某些懒加载初始化路径,保留偏向锁仍然有价值,需要结合实际压测数据和 Safepoint 日志综合权衡,而不是盲目跟风开关参数。

偏向锁撤销SafepointJVM锁优化修改时间:2026-09-13 07:14:29

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