JIT通过计数器识别热点代码:方法调用计数器默认10000次、回边计数器监控循环回跳,触发分层编译(C1快编译、C2深度优化),支持常规替换与OSR,并依运行时数据做寄存器分配、逃逸分析等优化。

JIT 提升性能的核心,是让真正高频执行的代码绕过解释器,直接以机器码运行——不是所有代码都编译,而是只编译“热”的那部分,并且越热优化越深。
热点代码怎么被识别出来
JVM 不靠猜,靠实时计数:
- 每个方法有独立的「方法调用计数器」,默认累计到 10000 次就触发编译(可通过
-XX:CompileThreshold调整) - 每个循环体维护「回边计数器」,每次跳回循环开头就 +1;哪怕方法只调用一次,但循环执行上万次,也会被标记为热点
- 计数器会定期衰减,避免历史冷代码长期占据编译队列
编译不是一步到位,而是分层推进
现代 HotSpot 默认启用分层编译,像流水线一样逐步加压优化:
- 第 0 层:纯解释执行,同时收集分支走向、对象分配、调用频次等运行时数据
- 第 2–3 层:C1 编译器介入,生成带基础优化(如简单内联、空值检查消除)的本地码,快但不激进
- 第 4 层:C2 编译器接手,基于完整 profile 数据做深度优化——循环展开、向量化(
-XX:+UseSuperWord)、逃逸分析、虚方法去虚拟化
变量和逻辑怎么被加速
加速不体现在语法上,而藏在运行时决策里:
立即学习“Java免费学习笔记(深入)”;
- 循环中的计数器(如
int i)和累加器(如long sum)会被优先分配到 CPU 寄存器,跳过栈内存读写 - 确认未逃逸的对象(如局部
new StringBuilder())可能被拆解为标量,或直接分配在栈上,消除堆分配与 GC 压力 - 分支判断结果高度稳定(如
if (status == SUCCESS)在 99.8% 请求中为 true),JIT 会折叠该分支或预加载主流路径
编译后的代码怎么生效
替换讲究时机,兼顾性能与语义安全:
- 常规替换:方法下次被调用时,直接跳转到 Code Cache 中的机器码执行
- 栈上替换(OSR):对正在执行的长循环,JVM 可中途暂停线程、重建栈帧、切到已编译版本继续跑,无需等方法返回
- 一旦运行时假设被打破(如新类加载导致多态分支变多),JVM 会触发去优化(deoptimization),退回到解释执行,再根据新数据重新编译



















