一 Java代码的执行分类

第一种是将源代码编译成字节码文件,然后在运行时通过解释器将字节码文件转为机器码执行。

第二种是编译执行(直接编译成机器码)。现代虚拟机为了提高执行效率,会使用即时编译技术(JIT,Just In Time)将方法编译成机器码后再执行。

HotSpot VM 是目前市面上高性能虚拟机的代表作之一。它采用解释器与即时编译器并存的架构。在 Java 虚拟机运行时,解释器和即时编译器能够相互协作,各自取长补短,尽力去选择最合适的方式来权衡编译本地代码的时间和直接解释执行代码的时间。

在今天,Java 程序的运行性能早已脱胎换骨,已经达到了可以和 C/C++ 程序一较高下的地步。

二 问题来了

有些开发人员会感觉到诧异,既然 HotSpot VM 中已经内置 JIT 编译器了,那么为什么还需要再使用解释器来“拖累”程序的执行性能呢?比如 JRockit VM 内部就不包含解释器,字节码全部都依靠即时编译器编译后执行。

JRockit 虚拟机是砍掉了解释器,也就是只采及时编译器。那是因为 JRockit 只部署在服务器上,一般已经有时间让他进行指令编译的过程了,对于响应来说要求不高,等及时编译器的编译完成后,就会提供更好的性能。

首先明确:

当程序启动后,解释器可以马上发挥作用,省去编译的时间,立即执行。

编译器要想发挥作用,把代码编译成本地代码,需要一定的执行时间。但编译为本地代码后,执行效率高。

所以:

尽管 JRockit VM 中程序的执行性能会非常高效,但程序在启动时必然需要花费更长的时间来进行编译。对于服务端应用来说,启动时间并非是关注重点,但对于那些看中启动时间的应用场景而言,或许就需要采用解释器与即时编译器并存的架构来换取一个平衡点。

在此模式下,当 Java 虚拟器启动时,解释器可以首先发挥作用,而不必等待即时编译器全部编译完成后再执行,这样可以省去许多不必要的编译时间。随着时间的推移,编译器发挥作用,把越来越多的代码编译成本地代码,获得更高的执行效率。

同时,解释执行在编译器进行激进优化不成立的时候,作为编译器的“逃生门”。

三 HotSpot JVM 执行方式

当虚拟机启动的时候,解释器可以首先发挥作用,而不必等待即时编译器全部编译完成再执行,这样可以省去许多不必要的编译时间。并且随着程序运行时间的推移,即时编译器逐渐发挥作用,根据热点探测功能,将有价值的字节码编译为本地机器指令,以换取更高的程序执行效率。

四 案例

注意解释执行与编译执行在线上环境微妙的辩证关系。机器在热机状态(稳定运行状态)可以承受的负载要大于冷机状态(刚刚启动状态)。如果以热机状态时的流量进行切流,可能使处于冷机状态的服务器因无法承载流量而假死。

在生产环境发布过程中,以分批的方式进行发布,根据机器数量划分成多个批次,每个批次的机器数至多占到整个集群的1/8。曾经有这样的故障案例:某程序员在发布平台进行分批发布,在输入发布总批数时,误填写成分为两批发布。如果是热机状态,在正常情况下一半的机器可以勉强承载流量,但由于刚启动的 JVM 均是解释执行,还没有进行热点代码统计和 JIT 动态编译,导致机器启动之后,当前1/2发布成功的服务器马上全部宕机,此故障说明了 JIT 的存在作用和意义。—阿里团队

 五 实战

1 代码

/**
* 测试JIT 编译器
*/
public class JITTest {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list.add("让天下没有难学的技术");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2 用 jconsole 查看 JIT 编译器

 3 用 jvisualvm 查看 JIT 编译器

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐