JVM执行引擎
虚拟机时一个相对于物理机的概念,这两种机器都有代码执行能力,其区别时物理机的执行引擎时直接建立在处理器、缓存、指令集和操作系统层面上的,而虚拟机的执行引擎则是由软件自行实现的,因此可以不受物理条件制约的定制指令集和执行引擎的结构体系,能够执行不被硬件直接支持的指令集格式。
JVM的主要任务是负责装载字节码到其内部,但字节码并不能够直接运行在操作系统之上,因为字节码指令并非等价于本地机器指令,它内部包含的仅仅只是一些能够被JVM锁识别的字节码指令、符号表、以及其他辅助信息。如果让一个Java程序运行起来,执行引擎的任务就是将字节码指令解释/编译为对应平台上的本地指令才可以。简单来说,JVM中执行引擎充当了将高级语言翻译为机器语言的译者。
前端编译与后端编译
前端编译,将Java程序编译为字节码文件
后端编译,负责将字节码翻译为操作系统能够识别的机器指令(执行引擎)
- 橙色部分为前端编译(javac)生成字节码文件
- 绿色部分为解释器执行
- 蓝色部分(后端编译)为JIT编译器执行
什么是解释器(Interpreter),什么是JIT编译器
解释器:当Java虚拟机启动时会根据预定义的规范堆字节码采用逐行解释的方式执行,将每条字节码文件中的内容翻译为对应平台的本地机器指令执行
JIT编译器,虚拟机将源代码直接编译成和本地机器平台相关的机器语言。
Java是半编译型半解释型语言
为什么Java需要前端翻译?
为什么保留解释器
- 当程序启动后,解释器可以马上发挥作用,生区编译的时间,立即执行。
- 编译器想要发挥作用,把代码编译成本地代码,需要一定的执行时间。但编译为本地代码后,执行效率搞高。
- 当Java虚拟机启动时,解释器可以首先发挥作用,而不必等待JIT编译器全部编译完成后再执行,这样可以省去许多不必要的编译时间。随着时间的推移,编译器发挥作用,把越来越多的代码编译成本地代码,获得更高的执行效率。
编译器
概念解释:
- Java语言的编译期其实是一段不确定的操作过程,因为它可能是一个前端编译器,将.java文件到.class文件的过程
- 也可能是指后端运行时编译器(JIT)把字节码转变为机器码的过程
- 还可能是静态提前编译器(AOT)直接把.java文件编译成本地机器代码的过程
如何选择
根据需要执行的代码被调用执行的频率而定。
-
一个被多次调用的方法,或者是一个方法体内部循环次数较多的循环体都可以被称为热点代码,因此都可以通过JIT编译器编译为本地机器指令。由于这种编译方法发生在方法的执行过程中,因此也被称为栈上替换(OSR).
-
一个方法究竟要被调用多少次,或者一个循环体究竟需要执行多少次循环才可以达到这个标准?必然需要一个明确的阈值,这里主要依靠热点探测功能。
-
目前HotSpot VM所采用的热点探测方式是基于计数器的热点探测。HotSpot VM将为每一个方法建立2个不同类型的计数器,本别为方法调用计数器和回边计数器。
热度衰减
-
如果不做任何设置,方法调用计数器统计的并不是方法被调用的绝对次数,而是一个相对的执行频率,即一段时间之内方法被调用的次数。当超过一定的时间限度,如果方法的调用次数仍然不足以让他提交给即时编译器编译,那这个方法的调用计数器就会被减少一半,这个过程称为方法嗲用计数器热度的衰减,而这点时间被称为半衰周期。
-
进行热度衰减的动作实在虚拟机进行垃圾回收时顺便执行的,可以使用虚拟机参数
-XX:-UseCounterDecay
来关闭热度衰减,让方法计数器统计方法调用的绝对次数,这样,只要系统运行时间足够长,绝大部分方法都会被编译成本地代码。 -
可以使用
-XX:CounterHalfLifeTime
参数设置半衰周期的时间,单位时s
HotSpot VM使用JIT分类
总结:
- JIT编译出来的机器码性能比解释器高
- C2编译器启动时长比C1编译器慢,系统稳定执行以后,C2编译器执行速度远远快于C1编译器。