Appearance
1、什么是方法内联?
内联代码:
java
public class InlineTest1 {
private static int add1(int x1, int x2, int x3, int x4) {
return add2(x1, x2) + add2(x3, x4);
}
private static int add2(int x1, int x2) {
return x1 + x2;
}
// 内联后
private static int addInline(int x1, int x2, int x3, int x4) {
return x1 + x2 + x3 + x4;
}
}
把目标方法的代码复制到发起调用的方法中,避免发生真实的方法调用。
2、使用方法内联的条件
- 方法体足够小
- 热点代码:如果方法体小于325字节会尝试内联,可用-XX:FreqInlineSize修改大小
- 非热点代码:如果方法体小于35字节,会尝试内联,可用-XX:MaxInlineSize修改大小
- 被调用方法运行时的实现被可以唯一确定
- static方法,private方法以及final方法,JIT可以唯一确定具体的实现代码
- public的实例方法,指向的方法可能是自身、父类、子类的代码,当且仅当JIT能够唯一确定方法的具体实现时,才有可能完成内联。
3、注意点
- 尽量让方法体小一些
- 尽量使用final、private、static关键字修饰方法,避免因为多态,需要对方法做额外的检查
- 一些场景下,可通过JVM参数修改阈值,从而让更多方法内联
4、内联可能产生的问题
- CodeCache的溢出,导致JVM退化成解释执行模式
5、方法内联相关JVM参数
参数名 | 默认 | 说明 |
---|---|---|
-XX:+PrintInlining | - | 打印内联详情,该参数需和-XX:+UnlockDiagnosticVMOptions配合使用 |
-XX:+UnlockDiagnosticVMOptions | - | 打印JVM诊断相关的信息 |
-XX:MaxInlineSize=n | 35 | 如果非热点方法的字节码超过该值,则无法内联,单位字节 |
-XX:FreqInlineSize=n | 325 | 如果热点方法的字节码超过该值,则无法内联,单位字节 |
-XX:InlineSmallCode=n | 1000 | 目标编译后生成的机器码代销大于该值则无法内联,单位字节 |
-XX:MaxInlineLevel=n | 9 | 内联方法的最大调用帧数(嵌套调用的最大内联深度) |
-XX:MaxTrivialSize=n | 6 | 如果方法的字节码少于该值,则直接内联,单位字节 |
-XX:MinInliningThreshold=n | 250 | 如果目标方法的调用次数低于该值,则不去内联 |
-XX:LiveNodeCountInliningCutoff=n | 40000 | 编译过程中最大活动节点数(IR节点)的上限,仅对C2编译器有效 |
-XX:InlineFrequencyCount=n | 100 | 如果方法的调用点(call site)的执行次数超过该值,则触发内联 |
-XX:MaxRecursiveInlineLevel=n | 1 | 递归调用大于这么多次就不内联 |
-XX:InlineSynchronizedMethods | 开启 | 是否允许内联同步方法 |
6、方法内联实践
java
public class InlineTest2 {
private static final Logger LOGGER = LoggerFactory.getLogger(InlineTest2.class);
public static void main(String[] args) {
long cost = compute();
// 方法内联了416ms add1:12byte add2:4byte
// 方法不内联580ms
LOGGER.info("执行花费了{}ms", cost);
}
private static long compute() {
long start = System.currentTimeMillis();
int result = 0;
Random random = new Random();
for (int i = 0; i < 10000000; i++) {
int a = random.nextInt();
int b = random.nextInt();
int c = random.nextInt();
int d = random.nextInt();
result = add1(a, b, c, d);
}
long end = System.currentTimeMillis();
return end - start;
}
private static int add1(int x1, int x2, int x3, int x4) {
return add2(x1, x2) + add2(x3, x4);
}
private static int add2(int x1, int x2) {
return x1 + x2;
}
}
设置不内联:-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:FreqInlineSize=1