Java 即时编译器(JIT,Just-In-Time Compiler)是 HotSpot VM 提升执行性能的核心机制。它把“冷启动时解释执行字节码”与“运行时把热点代码编译成本地机器码”结合起来,既保证了启动速度,又通过激进优化让长期运行的服务获得接近 C++ 的峰值性能。下面从原理、优化技术、调优实践与示例代码四个维度做一次系统性梳理。
阶段 | 说明 | 关键组件 |
|---|---|---|
1. 字节码加载 | .class → 方法区 | ClassLoader |
2. 解释执行 | 逐条字节码 -> 本地机器指令 | 模板解释器 |
3. 热点探测 | 统计调用次数/回边次数 | Interpreter、Profiling |
4. 即时编译 | 字节码 → SSA IR → 优化 → 机器码 | C1/C2 Compiler |
5. Code Cache | 缓存编译结果,后续直接跳转到机器码 | CodeHeap |
-XX:CompileThreshold)。编译器 | 特点 | 适用场景 |
|---|---|---|
C1(Client) | 启动快、优化保守 | GUI、短生命周期 |
C2(Server) | 峰值性能高、编译耗时大 | 服务端、长生命周期 |
Graal(JDK 10+) | 基于 Java 编写、支持 AOT、可插件化 | 云原生、静态镜像 |
Jaotc | 静态提前编译,减少 JIT 预热 | 无动态字节码的服务 |
b.value 的 getter 直接嵌入调用点,消除调用开销。# 开启分层编译(默认开)
-XX:+TieredCompilation
# 仅使用 C2
-XX:-TieredCompilation -server
# 打印编译过程
-XX:+PrintCompilation
# JIT 反汇编(需安装 hsdis)
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=print,*MyClass.hotMethod
# 调整 Code Cache
-XX:ReservedCodeCacheSize=256m
@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(2)
publicclass JITBenchmark {
@State(Scope.Thread)
publicstaticclass Holder {
int x = 1;
}
// 方法将被 C2 编译
@Benchmark
public int sum(Holder h) {
Point p = new Point(h.x, 2); // Point 未逃逸
return p.x + p.y;
}
// 优化后等价于直接 return 3
privatestaticclass Point {
finalint x, y;
Point(int x, int y) { this.x = x; this.y = y; }
}
}
运行:
mvn package
java -jar target/benchmarks.jar -prof perfasm
在 perfasm 输出中可观察到:
Point 对象被标量替换,无实际分配。sum 方法被内联,汇编里只剩 lea eax, [rdi+2] 一条指令。现象 | 原因 | 排查 |
|---|---|---|
Code Cache 满,退解释执行 | 方法过多、动态代理滥用 | -XX:+PrintCodeCache |
大量“made not entrant” | 去优化(De-optimization) | 查看 -XX:+TraceDeoptimization |
启动慢 | C2 编译阻塞 | 使用 -Xcomp 或 Graal AOT |
理解 JIT 的工作细节,不仅能写出更“对 JVM 胃口”的高性能代码,也能在出现性能问题时迅速定位根因。