tieredcompilation下c1与c2演进本质是方法从解释执行→c1编译→profiling→c2编译→替换的五级动态迁移过程,路径真实发生于code cache、nmethod、mdo等内存结构中,可被printcompilation、printtieredevents等参数观测验证。

要真正理解 TieredCompilation 下 C1 与 C2 的分层演进,关键不是背层级编号,而是看清方法代码在 JVM 内存中“走过的路”——从字节码落地、解释执行、C1 编译、profiling 数据沉淀,到最终被 C2 替换的完整物理路径。这条路径真实发生在 runtime 的 code cache、nmethod、MDO(MethodDataObject)等结构中,可观察、可验证、可追踪。
看懂方法的五级“生命周期”:从解释帧到本地机器码
每个 Java 方法在 HotSpot 中并非固定停留在某一层,而是随执行热度动态迁移:
- Level 0:方法首次调用时,仅由解释器执行;此时只在栈上分配解释帧,不生成任何本地代码,但已开始计数(method entry counter + backedge counter)
- Level 1–3:当调用次数或循环回边次数触达阈值(如默认 CompileThreshold=10000),C1 编译器介入,生成 nmethod(native method),存入 code cache;不同 level 对应不同 profiling 开关:level 1 无 profile,level 2 加入调用/回边计数,level 3 补充分支走向、接收者类型等 MDO 数据
- Level 4:C2 编译器监听 MDO 中的稳定信号(如某虚方法 95% 调用指向同一子类、循环展开因子收敛),触发异步编译;新生成的 C2 nmethod 具备逃逸分析、循环向量化、激进内联等能力,上线后自动替换原有 C1 版本(通过 deoptimization barrier 切换)
抓取真实演进痕迹:用 JVM 参数定位物理节点
不依赖猜测,直接观测方法在哪一层、是否升级、为何卡住:
-
-XX:+PrintCompilation:输出每次编译事件,格式如
123 45 b java.lang.String::hashCode (37 bytes),其中第三列b表示是后台线程编译,数字前缀是 compile ID,可关联后续日志 -
-XX:+UnlockDiagnosticVMOptions -XX:+PrintTieredEvents:显示方法在各 tier 间迁移的精确时刻和原因,例如
transition java.util.ArrayList::add from 0 to 1或upgrade java.lang.Math::max from 3 to 4 - -XX:+LogCompilation -XX:LogFile=jit.xml:生成结构化 XML,记录每个 nmethod 的 code blob 地址、指令长度、inlining 树、MDO 引用地址——这才是真正的“物理路径”证据
识别阻塞点:为什么有些方法永远停在 Level 3?
演进中断不是异常,而是 JVM 的主动风控。常见物理原因包括:
- code cache 空间不足:C2 编译产物更大,若 -XX:ReservedCodeCacheSize 过小(如默认 240MB),C2 编译请求会被静默丢弃,方法滞留在 C1+profiling 状态
- MDO 数据未达标:某方法虽调用频繁,但分支跳转高度随机(
branch_data波动大)、或虚方法接收者类型持续变化(virtual_call_data无主导类型),C2 拒绝编译以避免错误优化 - C2 队列积压:高并发服务中 C2 编译线程(默认 2 个)忙于处理其他热点,该方法在
CompileQueue中排队超时,JVM 主动降级为 C1 recompile(如从 level 3 → level 2)以保响应
动手验证路径:一个可复现的小实验
写一个简单循环方法,强制它走完全部五级:
- 定义方法
public static int hotLoop(int n) { int s = 0; for (int i = 0; i - 启动参数加 -XX:+PrintTieredEvents -XX:CompileThreshold=100(降低阈值便于观察)
- 循环调用 150 次触发 C1(level 1→2→3),再持续调用至 10 万次以上,观察日志中是否出现
upgrade ... to 4及对应 C2 compile ID - 用 jhsdb jstack --pid
查看该方法当前执行的是哪个 nmethod(address 匹配 jit.xml 中的 codeBlob)











