锁消除是JIT编译器在C2编译阶段基于逃逸分析跳过生成monitorenter/monitorexit指令的优化技术;要求对象未逃逸(局部new、不赋值静态字段、不传入外部方法、不作为返回值)、启用-XX:+DoEscapeAnalysis和-XX:+EliminateLocks,且方法为热点代码。

锁消除不是删掉 synchronized 关键字,而是 JIT 编译器在把字节码转成机器码时,跳过生成加锁和解锁指令——前提是逃逸分析确认对象“根本不会被其他线程看到”。
逃逸分析怎么判断一个对象“不逃逸”
JIT 在方法内联后做逃逸分析,检查对象引用是否离开当前线程或方法作用域:
- 对象是局部 new 出来的(比如
new StringBuffer()),没赋值给 static 字段、没传入外部方法(如logger.info(obj))、没放进全局集合(如map.put(key, obj)) - 对象没作为返回值传出方法,也没被写入 ThreadLocal 以外的共享结构
- 没被用于启动新线程(比如没传给
Thread.start()或Executor.submit())
满足什么条件 JIT 才会消除锁
光“不逃逸”还不够,还得满足运行时编译约束:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 必须启用逃逸分析:
-XX:+DoEscapeAnalysis(JDK8+ 默认开启) - 必须显式启用锁消除:
-XX:+EliminateLocks(它依赖逃逸分析,单独开无效) - 目标代码得是热点:方法被反复调用(比如循环十万次),触发 C2 编译器介入
- 锁对象要是局部、线程私有的,比如
this是局部 new 的实例,或锁的是方法内创建的Object、StringBuffer等
典型能被消除的代码例子
这些场景下,虽然写了 synchronized,但 JIT 编译后实际没锁操作:
立即学习“Java免费学习笔记(深入)”;
-
StringBuffer sb = new StringBuffer(); sb.append("a").append("b");——append是同步方法,但sb没逃逸,锁被完全省掉 -
Vector<String> v = new Vector<>(); v.add("x"); v.get(0);—— 仅方法内使用,不共享 - 自定义类中
synchronized void inc() { count++; },而该对象只在单个方法里 new 并调用
为什么本地跑代码常看不到效果
锁消除不是解释执行阶段发生的,也不是 C1 编译器做的事:
- 它只在 Server 模式、C2 编译器优化后的热点代码中生效
- 刚启动时是解释执行,锁还在;等方法被调用足够多次,JIT 才介入并可能消除
- 想验证是否生效,可加参数:
-XX:+PrintEscapeAnalysis -XX:+PrintCompilation,看日志里有没有lock elided或eliminated

















