锁消除是jvm在jit编译阶段通过逃逸分析发现对象未逃逸时自动省略monitorenter/monitorexit指令的优化,字节码保留synchronized但机器码无实际锁操作。

锁消除是 JVM 在运行时自动优化掉“实际上不需要”的 synchronized 同步操作,核心前提是:某个被加锁的对象**不会逃逸出当前线程的作用域**,即它只被单个线程访问,不存在多线程竞争可能。对局部对象做同步时,JVM 通过逃逸分析(Escape Analysis)识别出这一点,就能安全地移除锁开销。
锁消除发生的前提:逃逸分析确认对象不逃逸
逃逸分析是 JIT 编译器在方法调用层级上分析对象引用传播路径的技术。如果一个对象:
- 仅在方法内部创建(如 new StringBuffer()、new Object())
- 没有作为返回值传出
- 没有被赋值给静态字段、成员变量或传入可能跨线程的方法(如 Thread.start()、Executor.submit())
- 没有被发布到其他线程可见的容器(如全局 List、Map、队列等)
那么 JVM 就判定该对象“未逃逸”,其上的 synchronized 操作会被视为冗余,从而触发锁消除。
典型可消除场景:局部 StringBuffer/Vector 的同步方法调用
StringBuffer 的 append()、toString() 等方法都是 synchronized 的,但如果你只在方法内创建并使用它:
public String buildMessage() {
StringBuffer sb = new StringBuffer(); // 局部对象,无逃逸
sb.append("Hello");
sb.append(" ");
sb.append("World");
return sb.toString();
}
JVM 会发现 sb 完全局限于当前栈帧,不可能被其他线程看到,于是将每次 append() 内部的锁检查和 monitor 进入/退出操作全部省略。最终执行效果等价于直接使用 StringBuilder —— 没有同步开销,但语义完全安全。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
不是所有局部对象都能被消除:注意逃逸信号
即使对象是局部创建的,只要出现以下任一行为,逃逸分析就会失败,锁消除就不会发生:
- 把对象作为参数传给一个可能跨线程调用的方法(例如 submit(runnable)、offer(obj))
- 将对象赋值给 static 字段或 this 的成员变量
- 调用它的 wait()/notify()(这会强制关联 Monitor,阻碍消除)
- 在 synchronized 块中将该对象作为锁,并且该块被内联后仍存在跨方法边界的风险
例如:synchronized(obj) { obj.notify(); } 即使 obj 是局部变量,notify() 明确要求 Monitor 存在,JVM 通常不会消除这个锁。
如何验证锁是否被消除
可通过 JVM 参数开启逃逸分析和锁优化日志(需 Server VM):
-XX:+DoEscapeAnalysis -XX:+EliminateLocks -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintEscapeAnalysis
配合 JMH 基准测试对比 StringBuffer 与 StringBuilder 的吞吐量,若两者性能差异极小(比如
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










