锁消除和锁粗化是jvm jit编译器自动执行的两种锁优化技术:锁消除基于逃逸分析移除无竞争的锁,锁粗化则合并连续小粒度锁以减少开销。

锁消除和锁粗化是 JVM 在运行时由 JIT 编译器自动完成的两种锁优化技术,它们不改变程序语义,但能显著降低同步开销。关键在于:这些优化不是靠程序员手动加减 synchronized 实现的,而是 JVM 根据代码实际执行特征,在热点代码被 C2 编译后动态生效的。
锁消除:对象没逃逸,锁就白加
锁消除的本质是“发现锁根本没必要”。JIT 编译器借助逃逸分析(Escape Analysis)判断:某个被 synchronized 保护的对象是否只被当前线程访问、从未发布到其他线程或全局作用域。
满足以下条件时,锁可能被完全移除:
- 锁对象是方法内的局部变量(如 new Object()、StringBuilder、StringBuffer)
- 该对象未赋值给 static 字段、未传入未知方法(比如 logger.info(obj))、未放入 ThreadLocal 以外的共享容器(如 HashMap、ConcurrentLinkedQueue)
- 对象没有发生线程逃逸或方法逃逸
一旦确认无并发风险,JIT 不仅跳过 monitor enter/exit 指令,连对应的字节码都可能不生成——相当于这段代码从逻辑上“没加锁”。典型例子是单线程拼接字符串:new StringBuilder().append(...) 中 StringBuffer 的内部锁就被消除了。
锁粗化:把碎锁攒成一块锁
锁粗化针对的是高频、小粒度、连续且锁定同一对象的 synchronized 块。反复申请释放锁会带来可观的上下文切换与系统调用开销,JIT 会将相邻的多个细粒度锁合并为一个更大范围的锁。
常见可粗化的场景包括:
- 循环体内每次迭代都对同一个 lock 对象加锁
- 连续几行代码分别对同一对象加锁(如先 set 再 get 再 clear)
- 调用链中多个 synchronized 方法操作同一实例
例如:for (int i = 0; i 可能被优化为 <code>synchronized(lock) { for (...) { doWork(i); } }。注意:粗化前提是锁对象相同、无中间异步操作、无锁外依赖分支。
为什么本地测试常看不到效果
这两个优化只在 C2 编译器介入后的峰值性能阶段生效,不会出现在解释执行、C1 编译(客户端模式)或刚启动的冷代码中。
要验证或触发它们,需注意:
- 确保使用 server 模式(-server 或 JDK8+ 默认开启)
- 开启逃逸分析:-XX:+DoEscapeAnalysis(JDK8 起默认开启)
- 显式启用锁消除:-XX:+EliminateLocks(必须与逃逸分析同时开启)
- 让目标方法被频繁调用(如循环 10 万次以上),促使 JIT 将其编译为热点代码
没有达到编译阈值,或者对象发生了逃逸,优化就会跳过——这不是 bug,而是 JVM 的保守策略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











