飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

编译器优化:方法内联

时间:2021-12-26  作者:dtyy  

方法内联的思想是,把目标方法的代码复制代发起调用的方法之中,避免发生真实的方法调用。

public class InlineTest {
    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;
    }
}

如上代码,我们知道线程执行方法时,会向虚拟机栈压入栈帧,add1方法中调用了两次add2方法会压入两次add2的栈帧。频繁出入栈操作,消耗内存和时间。

JVM可以对上面的操作进行方法内联优化,优化为下面代码。

private static int add1(int x1, int x2, int x3, int x4) {
    return x1 + x2 + x3 + x4;
}

方法内联的条件有两个:

  1. 方法体足够小。

    1. 热点方法,如果方法体小于325字节会尝试内联,可以使用 -XX:FreqInlineSize修改大小。
    2. 非热点方法,如果方法体小于35字节尝试内联, -XX:MaxInlineSize
  2. 被调用的方法在运行时的实现可以被唯一确认。

    1. static、private、final方法,JIT可以唯一确认具体的实现代码。
    2. public实例方法,指向的实现可能是自身、父类、子类的代码(多态),只有当JIT唯一确认方法实现时,才有可能内联。

内联可能带来的问题:会导致方法变大,使得CodeCache溢出,导致JVM退化成解释执行模式。

一般情况,使用默认JVM参数就好。

测试方法内联

@Slf4j
public class InlineTest {
    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 long compute() {
        long start = 域名entTimeMillis();
        int result = 0;
        Random random = new Random();
        for (int i = 0; i < 10000000; i++) {
            result = add1(域名Int(), 域名Int(), 域名Int(), 域名Int());
        }
        long end = 域名entTimeMillis();
        return end - start;
    }

    public static void main(String[] args) {
        long compute = compute();
        域名("花费{}ms", compute);
    }
}

花费362ms
花费483ms

设置JVM参数,打印内联日志,开关内联(通过设置内联阈值)。

-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:FreqInlineSize=1

默认开启方法内联,比直接关掉运行更快。

JVM参数备注

image

标签:JVM编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。