Skip to content
文章目录

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=n35如果非热点方法的字节码超过该值,则无法内联,单位字节
-XX:FreqInlineSize=n325如果热点方法的字节码超过该值,则无法内联,单位字节
-XX:InlineSmallCode=n1000目标编译后生成的机器码代销大于该值则无法内联,单位字节
-XX:MaxInlineLevel=n9内联方法的最大调用帧数(嵌套调用的最大内联深度)
-XX:MaxTrivialSize=n6如果方法的字节码少于该值,则直接内联,单位字节
-XX:MinInliningThreshold=n250如果目标方法的调用次数低于该值,则不去内联
-XX:LiveNodeCountInliningCutoff=n40000编译过程中最大活动节点数(IR节点)的上限,仅对C2编译器有效
-XX:InlineFrequencyCount=n100如果方法的调用点(call site)的执行次数超过该值,则触发内联
-XX:MaxRecursiveInlineLevel=n1递归调用大于这么多次就不内联
-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