内联优化是将方法调用(如add(a, b))直接替换为方法体(如a + b),消除调用开销;它受字节码大小、方法修饰符、类型稳定性及内联深度限制,需通过jvm参数验证是否生效。

内联优化到底在干什么?
它不是“让方法跑得更快”,而是把 add(a, b) 这种调用,直接替换成 a + b —— 消掉方法调用本身那套栈帧压入、跳转、寄存器保存的“仪式”。对高频小方法来说,这一步省下的开销,可能比计算本身还大。
常见错误现象:你写了个 private static int add(int a, int b) { return a + b; },但压测时发现 sum = add(sum, i) 在循环里依然有可观的调用耗时 —— 很可能它没被内联,而不是代码写错了。
哪些方法能被JIT内联?看三件事
JIT(尤其是C2)不会无脑展开,它只信任满足条件的调用点:
-
-XX:MaxInlineSize=35是默认字节码大小上限(不是源码行数),超了基本不内联;final/private/static方法更容易过审 - 虚方法(比如普通实例方法)要靠类型分析:如果JIT发现某处
obj.calc()99% 调用的是FastImpl.calc(),且没见其他子类,就敢内联,并加个类型守卫(if (obj.getClass() != FastImpl.class) deoptimize) - 内联有深度限制:
-XX:MaxInlineLevel=9(默认),递归调用或链式调用太深会截断
怎么确认你的方法真被内联了?
别猜,用JVM参数实锤:
立即学习“Java免费学习笔记(深入)”;
- 加
-XX:+PrintInlining -XX:+UnlockDiagnosticVMOptions启动,控制台会打印类似:inline (hot) InlineDemo.add(II)I→ 表示成功内联too big或hot method too big→ 字节码超限not inlineable→ 可能是虚方法但类型不稳定 - 配合
-XX:+PrintCompilation看编译日志,找对应方法是否以3(C2编译级别)出现 - 用
JITWatch打开日志文件,可视化查看内联树和失败原因,比纯文本直观得多
为什么有时候内联反而变慢?
内联不是银弹,容易踩两个坑:
- 代码膨胀:一个30字节的方法被内联100次,机器码体积涨3KB,可能挤出CPU指令缓存(i-cache),导致更多缓存未命中,整体变慢
- 过度乐观的类型假设:JIT为单态虚调用内联后,突然来了个新子类实例,触发去优化(deoptimization),回退到解释执行,再重新编译 —— 这个过程有明显卡顿,线上偶发延迟飙升常源于此
真正关键的不是“能不能内联”,而是“JIT有没有足够稳定的运行时证据来安全内联”。冷启动后前几秒的数据不可信,观察至少稳定运行1–2分钟后的内联日志才靠谱。









