bigywater.tech
Java · 288

锁粗化与锁消除

连续几次对同一把锁加锁,JIT 会合并成一次吗?局部对象上的同步会被拿掉吗?这篇用开关对照量出每次调用的耗时,不靠猜,也看看字节码里到底有没有变化。

3 倍
同一锁连续 3 个 synchronized 块:关闭优化后 15.3 ns,默认 5.13 ns
≈10 倍
局部 Vector 两次 add:默认 2.6 ns 对比 -XX:-EliminateLocks 25.17 ns
6 个
含 3 个 synchronized 块的方法在字节码里的 monitorexit 数量(优化只在 JIT 里发生)

各种锁的每次调用耗时

实测 · 本机执行输出
  A 局部 StringBuffer 三次 append
    默认                                               18.28
    -XX:-EliminateLocks                              29.91
    -XX:-DoEscapeAnalysis                            42.21
    -XX:-EliminateLocks -XX:-DoEscapeAnalysis        32.02
    -Xint(解释执行,对照)                                   857.73
  B 同一共享锁连续 3 个 synchronized 块
    默认                                               5.13
    -XX:-EliminateLocks                              15.3
    -XX:-DoEscapeAnalysis                            5.11
    -XX:-EliminateLocks -XX:-DoEscapeAnalysis        15.3
  C 循环内每次迭代加锁(共享锁)
    默认                                               1.43
    -XX:-EliminateLocks                              5.14
    -XX:-DoEscapeAnalysis                            1.49
    -XX:-EliminateLocks -XX:-DoEscapeAnalysis        5.14
  D 锁一个局部 new Object()
    默认                                               1.92
    -XX:-EliminateLocks                              6.46
    -XX:-DoEscapeAnalysis                            6.48
    -XX:-EliminateLocks -XX:-DoEscapeAnalysis        6.43
  E 局部 Vector 两次 add
    默认                                               2.6
    -XX:-EliminateLocks                              25.17
    -XX:-DoEscapeAnalysis                            20.64
    -XX:-EliminateLocks -XX:-DoEscapeAnalysis        25.71

JDK 27 对照

实测 · 本机执行输出
  JDK 27  A 局部 StringBuffer 三次 a   默认                     12.94
  JDK 27  A 局部 StringBuffer 三次 a   -XX:-EliminateLocks    29.54
  JDK 27  B 同一共享锁连续 3 个 synchron   默认                     5.6
  JDK 27  B 同一共享锁连续 3 个 synchron   -XX:-EliminateLocks    16.99
  JDK 27  D 锁一个局部 new Object()     默认                     1.86
  JDK 27  D 锁一个局部 new Object()     -XX:-EliminateLocks    6.22

字节码

实测 · 本机执行输出
  方法 b:monitorenter 3 个,monitorexit 6 个(每个 synchronized 块 1 个 enter、2 个 exit:正常路径 + 异常路径)
  a(StringBuffer)的 invokevirtual append:3

分步演示

锁粗化与锁消除
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12 与 JDK 27,Apple Silicon;自写计时循环(没有用 JMH),每种配置各跑一个独立进程,取第 4–7 轮中位数
    • 没有验证:用 -XX:+PrintEliminateLocks 等诊断输出直接确认 JIT 的决策(没有使用)、竞争条件下的锁粗化代价(粗化可能延长持锁时间,没有测)、A 例中仅关逃逸分析反而最慢的原因、x86 上的数值、-XX:+UseHeavyMonitors 等其它锁选项
    京ICP备2026067502号-1京公网安备11010502063487号