启用-XX:+PrintInlining可直接观察JVM内联决策树,需配合-XX:+PrintCompilation等参数定位编译时机与拒绝原因,结合汇编输出或JMH验证实际效果。

启用 -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining 是观察 HotSpot JVM 内联行为最直接、最权威的方式。它不显示抽象图或统计汇总,而是逐行打印每个方法是否被内联、为何被拒绝、被哪一层编译器(C1/C2)处理、以及嵌套内联的完整决策链——也就是常说的“内联决策树”。关键在于理解日志结构、识别关键标记,并配合其他参数交叉验证。
日志解读核心:看懂三类关键行
启动时添加该参数后,JVM 在 C2 编译阶段(Tier 4)输出内联日志,每条记录以缩进体现调用层级。重点关注以下三类标记:
- @ 100:表示该方法在字节码偏移量 100 处被调用,是内联起点
- inline (hot) 或 inline (cold):说明内联已发生,“hot”代表因高频调用触发,“cold”多见于 C1 编译或强制内联场景
- did not inline (reason):明确拒绝原因,如 too big(代码体积超阈值)、hot method too big(热方法仍过大)、recursive(递归调用)、not compilable(含未支持字节码,如某些 invokedynamic 形式)
必须搭配的关键参数才能看清全貌
单独用 -PrintInlining 日志信息有限,需组合以下参数获取可操作结论:
-
-XX:+PrintCompilation:定位哪个方法在哪一时刻被哪一级编译器(C1/Tier 1–3 或 C2/Tier 4)编译,与内联日志时间戳对齐 -
-XX:MaxInlineSize=35 -XX:FreqInlineSize=325:显式设定期望的大小阈值,再对比日志中 “too big” 的实际字节码长度,验证是否真因尺寸被拒 -
-XX:+LogCompilation -XX:LogFile=inline.xml:生成结构化 XML 日志,可用 JITWatch 可视化展开“inlining tree”,直观看到父子方法关系与拒绝路径
典型内联失败场景与应对建议
常见拒绝原因不是配置错误,而是代码特征与 JVM 策略的客观冲突:
-
“too big” 但方法逻辑必要:检查是否含大量字符串拼接、冗余 try-catch 或未提取的重复表达式;可拆分为小方法,或用
@ForceInline(JDK 16+)标注关键热点小函数 -
“not osr”(On-Stack Replacement 拒绝):说明该方法已在解释执行中运行,且未达到 OSR 编译阈值;可通过
-XX:OnStackReplacePercentage=140调高比例(需同步调高-XX:CompileThreshold) -
“unstable if” 或 “unstable loop”:JIT 发现分支预测不稳定或循环边界难推断,常出现在动态条件、反射调用附近;应避免在热点路径中混入
Class.forName或Method.invoke
验证内联是否真正生效的硬指标
日志说“inline”,不代表最终机器码里就没了调用指令。最可靠验证方式是:
- 加
-XX:+PrintOptoAssembly(需 hsdis),搜索目标方法名是否还出现在汇编中;若已消失,且原位置变为寄存器运算或内存直接访存,即确认成功 - 用 JMH 写基准测试,对比开启/关闭内联(
-XX:-UseInline)的吞吐量变化;若提升超过 5%,基本可归因于内联收益 - 观察 GC 日志中的分配速率:成功内联常伴随逃逸分析生效,导致原本堆分配的对象被标量替换,从而降低
Allocation Rate

















