1 内联概念:把函数调用的方法直接内嵌到方法内部,减少函数调用的次数
2 函数的调用过程: 调用某个函数实际上将程序执行顺序转移到该函数所存放在内存中某个地址,将函数的程序内容执行完后,再返回到 转去执行该函数前的地方。这种转移操作要求在转去前要保护现场并记忆执行的地址,转回后先要恢复 现场,并按原来保存地址继续执行。也就是我们常说的压栈和出栈。
3 应用场景:当我们在调用频率很高的地方经常调用一个其他函数,但是函数体很小,函数频繁的函数跳转影响性能。这时候jvm能够自动识别热点函数,到达阀值触发jit优化,对其进行内联。
public class TT {
public int add(int a, int b) {
return a + b;
}
public static void main(String[] args) {
TT t = new TT();
for (int i = 0; i < 10; i++) {
System.out.println(t.add(i, i));
}
}
}
4 java内联配置
JVM会自动的识别热点方法,并对它们使用方法内联优化。那么一段代码需要执行多少次才会触发JIT优化呢?通常这个值由-
XX:CompileThreshold参数进行设置:
使用client编译器时,默认为1500;
使用server编译器时,默认为10000;
但是一个方法就算被JVM标注成为热点方法,JVM仍然不一定会对它做方法内联优化。其中有个比较常见的原因就是这个方法
体太大了,分为两种情况。
如果方法是经常执行的,默认情况下,方法大小小于325字节的都会进行内联(可以通过 -XX:MaxFreqInlineSize=N来设置这个大小)
如果方法不是经常执行的,默认情况下,方法大小小于35字节才会进行内联(可以通过 -XX:MaxInlineSize=N来设置这个大小)
我们可以通过增加这个大小,以便更多的方法可以进行内联;但是除非能够显著提升性能,否则不推荐修改这个参数。因为更大的方法体会导致代码内存占用更多,更少的热点方法会被缓存,最终的效果不一定好。
JVM参数:(-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining)
5 方法内联的规则:
(1)触发方法内联的规则: 1 自动拆箱总被内联
(2) 指令指定:-XX:CompileCommand中的inline指令指定的方法(c++);
(3)注解指定:@ForceInline注解的方法;
6 不触发方法内联的规则:
(1)Throwable类的方法不能被其他类中的方法内联;
(2)-XX:CompileCommand中dotinline或exclude指令指定的方法;
(3)@Dontinline注解的方法;
(4)调用字节码对应的符号引用未被解析;
(5)目标方法所在的类未被初始化;
(6)目标方法是native方法;
(7)C2不支持内联超过9层的调用(-XX:MaxInlineLevel),以及1层的直接递归调用(- XX:MaxRecursIninleLevel)