JVM虚拟机:JIT即时编译器优化之逃逸分析 | 方法内联

文章详细介绍了Java中的逃逸分析和JIT编译器的工作原理。解释器将字节码解释为通用机器码,而JIT会根据平台生成特定机器码,提高性能。热点代码是被频繁执行的部分,JIT通过C1和C2编译器进行优化。逃逸分析能识别无用对象创建,而方法内联则优化了短小且频繁调用的方法,减少调用开销。通过示例展示了内联和逃逸分析的优化效果。
摘要由CSDN通过智能技术生成

逃逸分析

JIT 和 解释器的区别

  • 解释器是将字节码解释为机器码,下次即使遇到相同的字节码,仍会执行重复的解释
  • JIT 是将一些字节码编译为机器码,并存入 Code Cache,下次遇到相同的代码,直接执行,无需再编译
  • 解释器是将字节码解释为针对所有平台都通用的机器码
  • JIT 会根据平台类型,生成平台特定的机器码

那么既然即时编译器速度远快于解释器,为什么不直接使用即时编译器呢,对于占据大部分的不常用的代码,我们无需耗费时间将其编译成机器码,而是采取解释执行的方式运行;另一方面,对于仅占据小部分的热点代码,我们则可以将其编译成机器码,以达到理想的运行速度。

什么是热点代码,就是【方法调用次数和循环次数达到了一定阈值】,就会成为热点代码

效率上看 解释器 < C1 编译器 < C2 编译器

C1 C2 的存在就是为了发现热点代码,这也是 JVM 虚拟机为什么叫 hotspot 的原因,请阅读下面的优化案例

public class JIT1 {

    // -XX:+PrintCompilation -XX:-DoEscapeAnalysis
    // 模拟编译器优化 JIT
    public static void main(String[] args) {
        for (int i = 0; i < 200; i++) { // 执行 200 次循环,每次村换创建 1000 个对象
            long start = System.nanoTime(); // 统计每一次创建 1000 个对象的执行时间
            for (int j = 0; j < 1000; j++) {
                new Object();
            }
            long end = System.nanoTime();
            System.out.printf("%d\t%d\n",i,(end - start)); // 循环次数 执行时间
        }
    }
}

输出

100 18773
101 47786
102 17493
103 22614
104 64427
105 18347
118 24320
119 19200
120 20053
130 20480
131 19627
132 20053
133 15360
134 136533
135 43093
136 853
137 853
138 853

因为 JIT 发现这些对象创建了又不用,还创建了这么多次,干脆在下次创建的时候就不创建了,这就叫逃逸分析,逃逸分析会在 C2 编译器执行

加一个虚拟机参数 -XX:-DoEscapeAnalysis

就不会执行逃逸分析了

方法内联

请阅读下面的代码

private static int square(final int i) {
    return i * i;
}

然后进行方法调用

System.out.println(square(9));

JIT 如果发现这个 square 方法比较短,只有 1 行,而且方法多次调用,就会进行内联,所谓的内联就是把方法内代码拷贝、粘贴到调用者的位置,如下

System.out.println(9 * 9);

又由于方法形参使用了 final 关键字,又会做常量折叠的优化

System.out.println(81);

请阅读以下优化案例

public class JIT2 {
    // -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining // 打印是否进行内联
    // -XX:CompileCommand=dontinline,*JIT2.square // 禁止某个方法内联

    public static void main(String[] args) {
        int x = 0;
        for (int i = 0; i < 500; i++) {
            long start = System.nanoTime();
            for (int j = 0; j < 1000; j++) {
                x = square(9);

            }
            long end = System.nanoTime(); // 多次循环后,耗费时长为 0,也就是内部执行的 1000 次循环都只会执行打印语句 System.out.print(81)
            System.out.printf("%d\t%d\t%d\n",i,x,(end - start));
        }
    }

    private static int square(final int i) {
        return i * i;
    }
}

输出

162	81	7200
163	81	8300
164	81	7300
165	81	8200
166	81	8000
167	81	8000
168	81	7500
169	81	9090
170	81	2050
171	81	100
172	81	0

细节请看源码中的注释

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值