JIT编译由运行时统计驱动,分层编译通过0–4五级模型渐进优化:0层解释执行,1–3层为C1不同profile编译,4层为C2/Graal深度优化;触发依赖方法调用(约1500/10000次)和回边计数(10000次)阈值,受线程负载、CodeCache容量等因素制约。

Java中JIT编译器的触发不是靠固定“开关”,而是由运行时统计驱动的动态决策。C1、C2(以及Graal)的介入时机取决于方法调用频次、循环执行次数等实时数据,而分层编译让这个过程变成渐进式优化——不是非此即彼,而是从解释执行一路升级到极致优化。
默认编译阈值:方法调用与回边计数
JVM通过两个核心计数器识别热点代码:
- 方法调用计数器:统计方法被调用的总次数。分层编译下,C1通常在约1500次调用后触发第1层编译(无profile),C2则在约10000次后启动深度编译;
- 回边计数器:专用于循环体,统计循环跳转(如for末尾跳回开头)的执行次数。当某循环回边超过10000次,即使所在方法调用不多,也会被标记为热点并触发OSR(On-Stack Replacement)编译。
这些阈值可调,例如-XX:CompileThreshold=5000会降低C2编译门槛,但实际生效受分层机制调控,并非简单替换。
分层编译的五级执行模型
现代HotSpot(JDK 8+默认启用)将执行划分为0–4共5个层级,每层对应不同编译策略与信息采集深度:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
立即学习“Java免费学习笔记(深入)”;
- 0层:纯解释执行——不编译、不采集任何运行时数据,仅逐条解释字节码;
- 1层:C1无profile编译——快速生成带基础优化(如公共子表达式消除)的本地码,不收集分支/类型等profile;
- 2层:C1有限profile编译——只记录方法调用和循环回边次数,用于缓解C2队列压力;
- 3层:C1完整profile编译——采集分支走向、虚方法实际调用类型等详细信息,为C2提供高质量优化依据;
- 4层:C2(或Graal)深度编译——基于profile做方法内联、逃逸分析、循环向量化、去虚拟化等激进优化,生成峰值性能机器码。
C2与Graal的替代关系及触发条件
Graal并非与C2并行存在,而是作为C2的可选替代者(JDK 10+引入,JDK 21起更成熟):
- 启用Graal需显式配置:-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler;
- 它复用相同的分层编译框架和阈值逻辑,但编译流程用Java实现,支持更灵活的优化策略和AOT能力;
- 触发Graal编译的条件与C2一致(同属第4层),但因编译器自身开销不同,实际编译延迟与成功率可能略有差异;
- 目前生产环境仍以C2为主流,Graal在部分计算密集型场景或多语言混合部署中展现优势。
影响阈值生效的关键因素
即使设定了-XX:CompileThreshold,真实编译时机还受以下约束:
- 后台编译线程负载:C2线程繁忙时,高热度方法可能先停留在3层C1,待资源空闲再升至4层;
- CodeCache容量:若-XX:ReservedCodeCacheSize不足,编译会被拒绝或降级,甚至触发“CodeCache is full”警告;
- 方法特征限制:过大方法(如超8000字节字节码)、含大量异常处理块、或已被标记为@HotSpotIntrinsicCandidate的特殊方法,可能跳过常规阈值逻辑;
- 预热要求:刚启动的应用需经历足够轮次的解释/C1执行,才能积累有效profile,否则C2无法做出精准优化决策。

















