相关系列
深入理解JVM后端优化技术-逃逸分析(Escape Analysis)-CSDN博客
深入理解JVM后端优化技术-锁消除(Lock Elision)-CSDN博客
深入理解JVM后端优化技术-锁粗化(Lock Coarsening)-CSDN博客
jvm只是负责依次将字节码指令逐次转换成机器码。而在转换过程中,JVM会对做一些代码优化,不管怎么样,就算程序员写出了很烂的代码,JVM也能保持一个不错的执行效率。这就是编绎优化。
方法内联定义
方法内联就是把被调用方的方法代码复制到调用方的方法中,避免真实的方法调用。这样可以频繁的减少创建栈帧。
示例1
代码
下面是示例代码:
package com.dzend.mall.order;
public class InLineDemo {
private int add(int x1,int x2,int x3,int x4){
return add2(x1,x2) + add2(x3,x4);
}
//内联优化
private int add2(int x1,int x2){
return x1+x2;
}
public static void main(String[] args) {
InLineDemo inLineDemo = new InLineDemo();
//超过⽅法调⽤计数器的阈值 100000 次,才会进⼊ JIT 实时编译,进⾏内联优化。
for(int i=0;i<100000;i++) {
inLineDemo.add(1, 2, 3, 4);
}
}
}
jvm参数设置
设置JVM参数:
-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions
执行过后可以看到
这个成为热点代码必须要达到JVM设置的阈值。当没有达到设置的阈值时,就不会看到方法内联了。
方法内联除了把目标方法的代码“复制”到发起调用的方法中,避免了真实的方法调用。然而,JVM方法内联过程没有这么简单。而且,方法内联还有更多的后续优化手段。
示例二
代码
看下面代码:
package com.dzend.mall.order;
public class InLineDemo {
private static void one(Object obj){
if(obj !=null){
System.out.printf("do something");
}
}
//内联优化,会把无用代码消除
private static void two(){
Object obj = null;
one(obj);
}
public static void main(String[] args) {
Long startTime = System.currentTimeMillis();
for(int i=0;i<1000000;i++) {
two();
}
System.out.println(">>>>>>>>"+(System.currentTimeMillis()-l));
}
}
代码分析
one和two是独立的二个方法,但是将one方法内联到two方法后,就可以把这个无用的死代码"Dead Code“。然后,JVM虚假机就可以进行dead code elimination死代码抹除的优化。
JDK8中JVM关于方法内联的参数
-XX:+Inline 启用方法内联。默认开启。
-XX:InlineSallCode=size 用来判断是否需要对方进行内联优化。如果一个方法编绎后的字节码大小于这个值,就无进行内联。默认值是1000bytes。
-XX:MaxInlineSize=size 设定内联方法的最大字节数。如果一个方法编绎后的字节码大于这个值,则无法进行内联。默认值是325bytes。
-XX:FreqInlieSize=size 设定热点方法进行内联的最大字节数。如果一个热点方法编绎后的字节大于这个值,则无法进行内联。默认值为325bytes
-XX:MaxTrivialSize=size 设定要进行内联的琐碎方法的最大字节数(Trivial Method:通常指那些只包含一两行语句,并且逻辑非常简单的方法。默认为6bytes.
-XX:+PrintInlining 打印内联决策,通过这个指令可以看到哪些方法进行了内联。默认是关闭的。另外,这个参数要配合-XX:+UnlockDiagnosticVMPOptions参数使用。
方法内联与编写代码技巧
比上面的相关参数不难分析出,编写代码的时候可以通过一些方法提升方法内联发生的概率。
1、在编程中,尽量多写小方法,避免写大方法。方法太大不光会导致方法无法内联。加外,成为热点代码后,还会占用更多的CodeCache。
2、在内存不紧张的情况下,可以通过调整JVM参数,减少热点阈值或增加方法体阈值,让更多的方法进行内联。
3、尽量使用final,private,static关键字修饰方法。该当如果需要继承(使用Invokevirtual指令调用) ,那具体调用 的方法,就只能在运行到这一行代码时才能确定,编绎器很难在编绎时得出绝对正确的结论,也就加大了编译执行的难度。