导读:本期聚焦于风铃创作的《如何手动禁用JVM偏向锁?高并发下减少锁撤销开销的启动参数调整》,敬请观看详情。JVM 偏向锁在高并发下真的会拖慢性能吗?当多个线程争抢同一把锁时,偏向锁的撤销操作需要进入全局安全点并暂停线程,这种隐形成本可能远高于直接使用轻量级锁。不少性能测试表明,在连接池、线程池等激烈竞争场景中,关闭偏向锁能显著降低安全点停顿和锁撤销开销。本文从偏向锁的对象头设计讲起,分析撤销过程为什么昂贵,并给出手动禁用的启动参数 -XX:-UseBiasedLocking。同时说明该参数在较新 JDK 版本中的默认变化,避免配置失效。文中还会结合锁状态查看方法和竞争代码示例,帮助读者判断自己的应用是否需要关闭偏向锁。

JVM 的偏向锁优化在没有竞争时非常高效,但它有一个明显的前提:锁对象从始至终只被一个线程访问。一旦这个前提被打破,偏向锁反而需要付出更高的撤销成本,尤其在高并发共享资源的场景下,频繁撤销带来的安全点停顿会直接影响吞吐量。因此很多性能调优实践会建议在竞争激烈的应用中手动关闭偏向锁,本文就围绕这一参数调整展开分析。

如何手动禁用JVM偏向锁?高并发下减少锁撤销开销的启动参数调整

一、偏向锁的设计初衷与撤销开销从何而来

偏向锁的核心思路很简单:如果一个锁对象从创建到回收始终只有一个线程访问,那么同步操作可以完全省略。偏向锁会在对象头的 Mark Word 中记录持有线程的 ID,并将对象标记为偏向状态。线程进入同步块时,只需要检查 Mark Word 中的线程 ID 是否与自己匹配,如果匹配就可以直接进入临界区,不需要执行 CAS 操作,也不需要进入内核态。

这种设计对单线程或极低竞争场景非常有利,能显著降低同步成本。但问题在于,当第二个线程尝试获取一个已经偏向其他线程的锁时,JVM 必须执行偏向撤销。偏向撤销并不是简单的指针修改,它需要进入全局安全点。在安全点期间,JVM 会暂停持有偏向锁的线程,检查该线程是否仍然处在同步块中,然后根据情况将锁恢复为无锁状态或升级为轻量级锁。

如果竞争频繁,这种撤销会被反复触发。每一次撤销都伴随着安全点停顿,即使临界区内的业务代码非常短,撤销开销也可能超过同步本身。因此在高并发下,偏向锁从最初的优化手段变成了性能负担,很多需要精细调优的 Java 服务会选择将它手动关闭。

二、高并发场景下偏向锁的典型性能问题

在实际应用中,连接池、线程池、缓存等共享资源往往会被大量线程同时访问,synchronized 同步块内的锁竞争非常激烈。默认情况下,当第一个线程访问锁对象后会获得偏向,随后的线程再访问时就会触发一次偏向撤销。如果线程切换频繁,JVM 还可能进入批量撤销或批量重偏向流程,这些操作都需要在安全点扫描线程栈,进一步放大停顿时间。

下面这段代码模拟了四个线程同时竞争一把锁的情况。如果在偏向锁开启的状态下运行,JVM 需要在每个线程第一次竞争时执行撤销,并且撤销过程会打断正常执行。虽然单个撤销时间可能只有几毫秒,但当并发量大、锁对象多的时候,累积开销会非常可观。

public class BiasedLockContention {
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[4];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new Runnable() {
                public void run() {
                    for (int j = 0; j < 200000; j++) {
                        synchronized (lock) {
                            // 临界区操作
                            int sum = j + 1;
                        }
                    }
                }
            });
            threads[i].start();
        }
        for (Thread t : threads) {
            t.join();
        }
        System.out.println("finished");
    }
}

在这类场景中禁用偏向锁,可以让锁在第一次发生竞争时直接进入轻量级锁状态。轻量级锁通过 CAS 自旋来竞争,虽然也有一定的 CPU 消耗,但不会频繁触发安全点,也不会产生偏向撤销的额外停顿。从停止世界时间和延迟稳定性角度看,关闭偏向锁往往更有利。

三、手动禁用偏向锁的启动参数与验证

要手动禁用偏向锁,可以在启动 JVM 时添加参数 -XX:-UseBiasedLocking。JVM 的布尔参数使用加号表示开启,减号表示关闭,因此 -XX:-UseBiasedLocking 表示禁用偏向锁。例如在启动一个独立 Java 应用时,可以这样配置:

java -XX:-UseBiasedLocking -Xms512m -Xmx512m -jar app.jar

需要注意的是,偏向锁的默认状态在不同 JDK 版本中并不相同。JDK 8 和 JDK 11 中偏向锁默认开启,因此手动禁用对高并发应用有实际意义。而从 JDK 15 开始,偏向锁默认被关闭,并且相关参数被标记为弃用,在更新的 JDK 版本中这个参数可能不再产生实际效果。配置前应当先确认运行环境的 JDK 版本,避免无效调优。

如果希望验证偏向锁是否真的被关闭,可以使用 JOL 工具打印对象头信息。下面的示例通过 ClassLayout 输出锁对象的 Mark Word 状态,偏向锁关闭后的对象会直接显示为无锁状态。

import org.openjdk.jol.info.ClassLayout;

public class LockHeaderCheck {
    public static void main(String[] args) {
        Object lock = new Object();
        System.out.println(ClassLayout.parseInstance(lock).toPrintable());
    }
}

除了查看对象头,也可以通过 jcmd 命令查看虚拟机参数,确认 UseBiasedLocking 的值是否为 false。合理验证参数是否生效,是避免配置错误的重要步骤。

四、禁用偏向锁后的优化思路与注意事项

关闭偏向锁并不意味着所有场景都能获得性能提升。如果应用中的锁竞争非常低,或者大量锁对象只被单一线程使用,偏向锁仍然能够减少同步操作,此时盲目禁用反而会增加轻量级锁的 CAS 开销。因此是否禁用偏向锁,应该基于压测结果来决定,而不是直接套用某个固定配置。

除了调整启动参数,还可以从代码层面降低锁竞争。缩小 synchronized 块的范围、减少锁对象的共享、使用无锁数据结构或者拆分锁粒度,这些优化比单纯调整 JVM 参数更持久有效。JVM 的锁消除和锁粗化也会在运行时对同步代码进行优化,开发时尽量避免在热点路径上使用粗粒度同步。

如果应用已经升级到较新的 JDK 版本,偏向锁默认就是关闭的,此时不需要再手动添加参数。但在使用老版本 JDK 的服务中进行性能调优时,-XX:-UseBiasedLocking 仍然是一个简单有效的启动参数,尤其适合锁竞争激烈的在线服务或中间件系统。

JVM偏向锁锁撤销开销启动参数调整修改时间:2026-08-21 07:32:13

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