java虚拟机JVM执行引擎(工作过程、JIT等)
文章目录
- 11. 执行引擎
- 11.1 执行引擎概述
- 11.2 执行引擎的工作过程
- 11.3 java代码编译和执行的过程
- 11.4 JIT编译器
- 11.5 方法调用计数器
- 11.6 回边计数器
- 11.6 HotSpot JM中JIT分类
- 11.7 总结
11. 执行引擎
11.1 执行引擎概述
执行引擎是java虚拟机核心的组成部分之一。
虚拟机的执行引擎则是由软件自行实现的。
执行引擎(Execution Engine)的任务就是将字节码指令解释/编译为对应平台上的本地机器指令才可以。
11.2 执行引擎的工作过程
- 执行引擎在执行的过程中究竟需要执行什么样的字节码指令完全依赖于PC寄存器。
- 每当执行完一项指令操作后,PC寄存器就会更新下一条需要被执行的指令地址。
- 当然方法在执行的过程中,执行引擎有可能会通过存储在局部变量表中的对象引用准确定位到存储在Java堆区中的对象实例信息,以及通过对象头中的元数据指针定位到目标对象的类型信息。
11.3 java代码编译和执行的过程
解释器:当Java虚拟机启动时会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码文件中的内容“翻译”为对应平台的本地机器指令执行。
JIT (Just In Time Compiler)编译器:就是虚拟机将源代码直接编译成和本地机器平台相关的机器语言。
为什么说java是半编译半解释?
执行java代码时,通常通常都会将解释执行和编译执行二者结合。
机器码:
- 各种用二进制编码方式表示的指令,叫做机器指令码。开始,人们就用它采编写程序,这就是机器语言。
- 机器语言虽然能够被计算机理解和接受,但和人们的语言差别太大,不易被人们理解和记忆,并且用它编程容易出差错。
- 用它编写的程序一经输入计算机,CPU直接读取运行,因此和其他语言编的程序相比,执行速度最快。
- 机器指令与CPU紧密相关,所以不同种类的CPU所对应的机器指令也就不同。
指令:
- 指令就是把机器码中特定的0和1序列,简化成对应的指令。
- 不同平台的同一种指令对应的机器码可能不一样。
指令集:
- 不同的硬件平台,各自支持的指令,是有差别的。因此每个平台所支持的指令,称之为对应平台的指令集。
- 常见的有:x86指令集和ARM指令集。
11.4 JIT编译器
- HotSpot VM采用解释器与即时编译器并存的架构。
为什么要保留解释器来“拖累”程序的执行性能?
程序刚启动的时候,解释器就会立即执行,响应速度快,各有优缺点。
选择JIT编译还是解释器?
当然是否需要启动JIT编译器将字节码直接编译为对应平台的本地机器指令,则需要根据代码被调用执行的频率而定。关于那些需要被编译为本地代码的字节码,也被称之为“热点代码”,JIT编译器在运行时会针对那些频繁被调用的“热点代码”做出深度优化,将其直接编译为对应平台的本地机器指令,以此提升Java程序的执行性能。
- 一个被多次调用的方法,或者是一个方法体内部循环次数较多的循环体都可以被称之为“热点代码”,因此都可以通过JIT编译器编译为本地机器指令。调用多少次能达到标准,依靠热点探测功能。目前HotSpot VM所采用的热点探测方式是基于计数器的热点探测。
HotSpot VM为每个方法建立两个计数器,分为方法调用计数器和回边计数器。
方法调用计数器用于统计方法的调用次数。
回边计数器则用于统计循环体执行的循环次数。
11.5 方法调用计数器
- 统计方法被调用的次数,默认阈值在Client模式下是1500次,Sever模式下是10000次,超过这个阈值就会触发JIT编译。
如果不做任何设置,方法调用计数器统计的并不是方法被调用的绝对次数,而是一个相对的执行频率,即一段时间之内方法被调用的次数。当超过一定的时间限度,如果方法的调用次数仍然不足以让它提交给即时编译器编译,那这个方法的调用计数器就会被减少一半,这个过程称为方法调用计数器热度的衰减(Counter Decay),而这段时间就称为此方法统计的半衰周期(counter Half Life Time)。
-XX:-UseCounterDecay
来关闭热度衰减。
-XX:CounterHalfLifeTime
参数设置半衰周期的时间,单位是秒。
11.6 回边计数器
- 它的作用是统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边”(Back Edge)。显然,建立回边计数器统计的目的就是为了触发OSR编译。
-Xint
:完全采用解释器模式执行程序;
-xcomp
:完全采用即时编译器模式执行程序。如果即时编译出现问题,解释器会介入执行。
-Xmixed
:采用解释器+即时编译器的混合模式共同执行程序。
11.6 HotSpot JM中JIT分类
在HotSpot VM中内嵌有两个JIT编译器,分别为client Compiler和serverCompiler,但大多数情况下我们简称为C1编译器和C2编译器。
-client
:指定Java虚拟机运行在client模式下,并使用C1编译器。C1编译器会对字节码进行简单和可靠的优化,耗时短。以达到更快的编译速度。-server
:指定Java虚拟机运行在server模式下,并使用C2编译器。C2进行耗时较长的优化,以及激进优化。但优化的代码执行效率更高。64位HotSpot是server模式
C1和C2编译器不同的优化策略:
在不同的编译器上有不同的优化策略,c1编译器上主要有方法内联,去虚拟化、冗余消除。
- 方法内联:将引用的函数代码编译到引用点处,这样可以减少栈帧的生成,减少参数传递以及跳转过程
- 去虚拟化:对唯一的实现类进行内联
- 冗余消除:在运行期间把一些不会执行的代码折叠掉
C2的优化主要是在全局层面,逃逸分析是优化的基础。基于逃逸分析在c2上有如下几种优化:
- 标量替换:用标量值代替聚合对象的属性值
- 栈上分配:对于未逃逸的对象分配对象在栈而不是堆
- 同步消除:清除同步操作,通常指synchronized
11.7 总结
- 一般来讲,JIT编译出来的机器码性能比解释器高。
- C2编译器启动时长比c1编译器慢,系统稳定执行以后,c2编译器执行速度远远快于c1编译器。