执行引擎是java虚拟机核心组成之一
“虚拟机”是一个相对于“物理机”的概念,这两种机器都有执行代码的能力,其区别是物理机的执行引擎是建立在处理器、缓存、指令集、和操作系统层面上的,而虚拟机的执行引擎则是由软件自行实现的,因此可以不受物理条件制约的定制指令集和执行引擎的结构体系,能够执行那些不被硬件直接支持的指令集格式
JVM的主要任务是负责装载字节码到内部,但字节码并不能直接运行在操作系统上,因为字节码并非等价于本地机器指令,它内部包含的仅仅只是一些能被JVM识别的字节码指令、符号表、以及其他辅助信息
那么如果让一个java程序运行起来,执行引擎的任务就是将字节码指令解释/编译为对应平台的本地机器指令才可以。简单来说,JVM中的执行引擎充当了将高级语言翻译为机器语言的译者。
什么是解释器,什么是JIT编译器
解释器:当java虚拟机启动时会根据预定义的规范对字节码采用逐行解释的方式执行,当每条字节码文件的内容“翻译”为对应平台的本地机器指令执行
JIT编译器:虚拟机将源代码直接编译成本地机器平台相关的机器语言
解释器
解释器镇长意义上所承担的角色就时一个运行时“翻译者”,将字节码文件中的内容“翻译”为对应平台的本地机器指令执行。
当一条字节码指令被解释执行完成后。接着再根据pc寄存器中记录的下一条需要被执行的字节码指令执行解释操作。
为什么解释器和JIT编译器同时使用
程序启动后,解释器可以马上发挥作用,省去编译时间,立即执行。
编译器要想发挥作用,把代码编译为本地代码,需要一定执行时间。但编译为本地代码后执行效率高
所以:
java虚拟机启动时,解释器可以首先发挥作用,而不必等待即时编译器全部编译完成后在执行,这样省去很多不必要的编译时间。随着时间的推移,编译器发挥作用,越来越多的代码编译为本地代码,获得更高执行效率
同时,解释执行在编译器进行激进优化不成立的时候,作为编译器的逃生门
JIT编译器
java语言中“编译器”其实是一段“不确定”的操作过程,因为他可能是指前端编译(其实叫前端的编译更确切一点)把.java文件转变成.class文件过程;
也可能是指虚拟机的后端运行期编译器(JIT编译器)把字节码转换成机器码的过程
还可能是指静态提前编译(AOT编译器)直接把.java文件编译为本地机器代码的过程
热点代码以及探测方式
是否启动JIT编译器将字节码直接编译为对应平台的本地机器指令,需要根据代码被调用执行的频率而定。关于需要被编译为本地代码的字节码被称为热点代码,JIT编译器在运行时会针对那些频繁被调用的热点代码做出深度优化,直接编译为本地机器指令,提高效率
一个被多次调用的方法,或是一个方法体内部循环次数较多的循环体都可以被称为热点代码,因此都可以通过JIT编译器编译为本地机器指令。由于这种编译方式发生在方法的执行过程中,因此被称为站上替换
一个方法究竟要被调用多少次,或者一个循环体究竟需要执行多少次循环才可以达到这个标准?必然需要一个明确的阈值,JIT编译器才会将这些热点代码编译为本地机器指令执行,主要依靠热点探测功能
目前HotSopt VM所采取的热点探测方式是基于计数器的热点探测
热度衰减
如果不做任何设置,方法调用计数器统计的并不是方法被调用的绝对次数,而是一个相对的执行频率,既一段时间内方法被调用的次数,当超过一定的时间限度,如果方法的调用次数仍然不足以让他提交给即时编译器,那这个调用计数器就会减少一半,这个过程被称为衰减,这段时间被称为半衰期
JIT分类
HotSopt VM中内嵌两个JIT编译器,分别为Client Compiler和Server Compoler,简称c1,c2
-client:指定虚拟机运行在client环境下,并使用c1编译器。c1编译器对字节码进行简单可靠的优化,耗时短,编译速度快
-server:指定虚拟机运行在server环境下,并使用c2编译器,c2编译器耗时长,优化激进,但优化后效率更高
64位版本固定为server,设置client将被忽略
c1编译器优化策略
方法内联:引用的函数代码编译到引用点处,减少栈帧的生成,减少参数传递和跳转的过程
去虚拟化:对唯一的实现类进行内联
冗余消除:在运行期将不执行代码折叠
c2编译器优化策略
标量替换:用标量值替换聚合对象属性值
栈上分配:对于未逃逸的对象分配对象在栈而不是堆
同步消除:自动清除没必要同步操作(比如加锁)