前言
- 执行引擎是Java虚拟机的核心组成部分之一。
- JVM的主要任务是负责装载字节码到其内部,但字节码并不能够直接运行在操作系统之上,因为字节码指令并非等价于本地机器指令,它内部包含的仅仅只是一些能够被JVM所识别的字节码指令、符号表和其他辅助信,那么,如果想让一个Java程序运行起来、执行引擎的任务就是将字节码指令解释/编译为对应平台上的本地机器指令才可以。
- 简单来说,JVM中的执行引擎充当了将高级语言翻译为机器语言的译者。
- 执行引擎在JVM中的结构如下图所示,在左下角。
1.两种执行器
Java虚拟机的执行引擎子系统中包含两种执行器,分别为解释器和即时编译器。当执行引擎获取到由javac编译后的.class
字节码文件后,在运行时是通过解释器(Interpreter)转换成最终的机械码执行。另外为了提升效率,JVM加入了一种名为 JIT即时编译 的技术,即时编译器的目的是为了避免一些经常执行的代码被解释执行,JIT会将整个函数编译为平台本地的机械码,从而在很大程度上提升了执行的效率。
1.1、解释器(Interpreter)
当Java程序运行时,在执行一个方法或某处代码时,会找到.class
文件中对应的字节码,然后会根据定义的规范,对每条需执行的字节码指令逐行解释,将其翻译成平台对应对应的本地机械码执行。当一条字节码指令被解释执行完成后,紧接着会再根据PC寄存器(程序计数器)中记录的下一条需被执行指令,读取并再次进行解释执行操作。
在HotSpot虚拟机中,解释器主要由Interpreter模块和Code模块构成,Interpreter模块实现了解释执行的核心功能,Code模块主要用于管理解释器运行时生成的本地机械指令。
解释器分为两种类,模板解释器和C++字节码解释器如下:
本篇文章先入个门,先简单的了解一下,之后细讲这一块。
1.2、JIT即时编译器(Just In Time Compiler)
在JVM运行过程中采用的解释器+编译器混合执行的模式,一般是指JIT编译器,在HotSpot虚拟机中内嵌着两个JIT即时编译器,分别为Client Compiler
与Server Compiler
,也就是通常所说的C1和C2编译器,JVM在64位的系统中默认采用的C2编译器,也就是Server Compiler
编译器。不过同样的,在程序启动的时候也可以通过参数显式指定运行时到底采用哪种编译器,如下:
- -client:指定JVM运行时采用C1编译器。
- C1编译器会对字节码进行简单和可靠的优化,耗时比较短,追求编译速度。
- -server:指定JVM运行时采用C2编译器。
- C2编译器会对字节码进行激进优化,耗时比较长,追求编译后的执行性能。
由于解释器实现简单,并且具备非常优异的跨平台性,所以现在的很多高级语言都采用解释器的方式执行,比如Python、Rust、JavaScript
等,但对于编译型语言,如C/C++、Go
等语言来说,执行的性能肯定是差一筹的,而前面不止一次提到过:Java为了解决性能问题,所以采用了一种叫做JIT即时编译的技术,也就是直接将执行比较频繁的整个方法或代码块直接编译成本地机器码,然后以后执行这些方法或代码时,直接执行生成的机器码即可。
OK~,那么对于上述中 执行次数比较频繁的代码 判断基准又是什么呢?答案是:热点探测技术。
2.热点代码探测技术
HotSpot VM的名字就可以看出这是一款具备热点代码探测能力的虚拟机,所谓的热点代码也就是指调用次数比较多、执行比较频繁的代码,当某个方法的执行次数在一定时间内达到了规定的阈值,那么JIT则会对于该代码进行深度优化并将该方法直接编译成当前平台对应的机器码,以此提升Java程序执行时的性能。
一个被多次调用执行的方法或一处代码中循环次数比较多的循环体都可以被称为 热点代码 ,因此都可以通过JIT编译为本地机器指令。
2.1 栈上替换OSR(On Stack Replacement)
纵观所有编程语言,类似于C/C++、GO
等编译型语言,都属于静态编译型,也就是指在程序启动时就会将所有源代码编译为平台对应的机器码,但JVM中的JIT却属于动态编译器,因为对于热点代码的编译是发生在运行过程中的,所以这种方式也被称