锁消除不是删除synchronized关键字,而是JIT编译器基于逃逸分析判定对象未逃逸后,在生成机器码时跳过monitorenter/monitorexit指令;它要求对象不返回、不赋静态变量、不传跨线程方法、不写入已逃逸对象字段,且需C2编译器触发热点编译并启用-XX:+DoEscapeAnalysis和-XX:+EliminateLocks。

锁消除不是删掉 synchronized 关键字,而是 JIT 编译器在生成机器码时,跳过对未逃逸对象的加锁/解锁指令。它完全依赖逃逸分析的结果来决策——没有逃逸分析,锁消除就不会发生。
逃逸分析是锁消除的唯一判断依据
JVM 通过逃逸分析静态扫描对象的使用路径,只有当对象被判定为“未逃逸”时,才进入锁消除候选流程。未逃逸意味着该对象:
- 没作为方法返回值暴露出去
- 没赋值给 static 变量、实例字段或全局容器(如缓存 Map)
- 没传入可能跨线程执行的方法(如 executor.submit()、new Thread().start())
- 没被写入其他已逃逸对象的字段中
只要违反其中任意一条,对象就被标记为逃逸,锁消除立即失效。
锁消除只发生在 JIT 编译阶段
它不改变字节码,也不影响源码语义。synchronized 关键字和 monitorenter/monitorexit 指令依然存在,但 C2 编译器在生成本地机器码时选择不翻译这些指令。这个过程需要:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
- 方法被识别为热点(默认调用约 10000 次触发 C2 编译)
- 启用逃逸分析(-XX:+DoEscapeAnalysis,默认开启)和锁消除开关(-XX:+EliminateLocks)
- 避开干扰因素:比如调用 native 方法、System.out.println()、Lambda 捕获、反射等
典型可消除场景与验证方式
常见易触发锁消除的代码包括:
- StringBuffer sb = new StringBuffer(); sb.append("a"); —— append 是 synchronized,但 sb 局部创建且未逃逸
- synchronized (new Object()) { ... } —— 锁对象生命周期仅限当前同步块
- Counter c = new Counter(); c.increment(); —— 自定义 synchronized 方法,c 完全封闭在栈帧内
验证是否生效不能看字节码,而应:
- 加参数 -XX:+PrintEscapeAnalysis -XX:+PrintCompilation,观察日志中是否出现 lock elided 或 eliminated synchronized
- 用 JMH + perfasm 查看汇编输出,确认 monitorenter 指令是否消失
- 对比开启/关闭逃逸分析(-XX:-DoEscapeAnalysis)的性能差异,通常耗时上升约 15%

















