JVM执行引擎

执行引擎
  • 执行引擎是JAVA虚拟机核心的组成之一
  • 虚拟机是一个相对与物理机的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是建立在 处理器, 缓存,指令集和操作系统层面上的,而虚拟机的执行引擎则是有软件自行实现的。因此可以不必受到物理条件制约的定制指令集与执行引擎的结构体系,能够执行那些不被硬件直接支持的指令集格式。
  • JVM的主要任务是负责装载字节玛到其内部,但字节玛并不能直接运行在操作系统之上,因此字节玛指令并非等价于本地机器指令,他包含内部的仅仅只是能够被JVM所识别的字节玛指令, 符号表,以及其他的辅助信息。那么如果想让一个JAVA程序运行起来执行引擎(Execution Engine) 的任务就是将字节玛指令解释/编译为对应平台上的本地机器指令才可以。简单来说,JVM的执行引擎充当了将高级语言翻译为机器语言的翻译官。

Java代码编译和执行的过程

在这里插入图片描述

大部分的程序代码转换成物理机的目标代码或虚拟机能够执行的指令集之前,都会需要经过上图的各个步骤

什么是解释器(Interpreter), 什么是JIT编译器?

解释器:当JAVA虚拟机启动时,会根据预定义的规范对字节码才用逐行解释的方式执行,将每条字节码文件中的内容 翻译为对应本地平台的本地机器指令执行。
JIT(Just In Time Compiler) 编译器: 就是虚拟机将源代码直接编译成和本地机器平台相关的机器语言。

为什么说Java 是半编译 半解释型语言?

JDK1.0 时代,Java语言定义为 解释执行 还是比较准确的。 再后来,Java 也发展出可以直接生成本地代码的编译器。
现在JVM在执行JAVA代码的时候,通常都会将解释执行与编译执行二者结合起来进行。

在这里插入图片描述

解释器
  • 解释器真正意义上所承担的角色就是一个运行时的翻译者,将字节码文件中的内容“翻译”,为对应平台的本地机器指令执行
  • 当下一条字节码指令被解释执行完成后,接着在根据PC寄存器中记录的下一条需要被执行的字节码指令执行解释操作。
解释器分类
  • 字节码解释器:
    • 字节码解释器在执行的时候通过纯软件代码模拟字节码的执行,效率非常低
  • 模板解释器:
    • 将每一条字节码和一个模板函数相关联,模板函数中直接产生这条字节码执行时的机器码,从而很大程度上提高了解释器的性能。
    • 在HotSpot VM中,解释器主要是由 Interpreter 模块 和 Code 模块构成。
      • Interpreter 模块: 实现了 解释器的核心功能
      • Code 模块:用于管理HotSpot VM 在运行时生成本地机器指令
JIT 编译器

概念解释:

  • Java 语言的编译期, 其实是一段 不确定的操作过程,因为它可能是指一个前端编译器(其实叫做 编译器的前端 更加准确一些。)把.java 文件转换成.class 文件的过程。
  • 也可能是指虚拟机的后端运行编译器(JIT编译器, Just In Time Compiler) 把字节玛转换成机器码的过程。
  • 还可能是使用静态提前编译器(AOT 编译器, Ahead Of Time Compiler) 直接把 .java 文件编译成本地机器代码的过程

前端编译器:Sun 的javac Eclipse JDT 中的增量式编译器(ECJ)。
JIT编译器: HotSpot VM 的C1 C2 编译器
AOT编译器: GUN Compiler for the Java(GCJ), Excelsior JET

为什么解释器和JIT编译器同时存在
  • 当程序启动后,解释器可以马上发挥作用,省区编译的事件,立即执行,编译器要想发挥作用,把代码编译为本地代码,需要执行一段时间,但编译为本地代码后,执行效率高。
  • 所以尽管 JRockit VM 程序的执行性能会非常高效,但是程序在启动时候必然需要花费更多的事件来进行编译,对于服务端应用来说,启动时间并非是关注点,但是对于哪些看重启动时间的应用场景下,获取就需要才用解释器与即时编译器并存的架构来换取一个平衡点。在此模式下,当Java虚拟机启动时,解释器可以首先发挥作用,而不必等待即时编译器全部编译完之后再来执行。 这样可以省去许多不必要的编译事件,随着时间的推移,编译器发挥作用,把越来越多的代码编译成本地代码,获得更高的执行效率。
  • 同时,解释执行在编译器进行激进优化不成立的时候,作为编译器的逃生门。
热点代码及探测方式。

是否需要JIT编译器将字节玛直接编译为对应平台的本地机器指令,则需要根据代码被调用执行的频率而定。关于那些需要被编译为本地代码的字节玛,也被称之为热点代码, JIT 编译器在运行时会针对那些频繁被调用的 热点代码 作出深度油画,将其直接编译为对应平台的本地机器指令,以此提升Java程序的执行性能。

探测方式:
  • 一个被多次调用的方法,或者是一个方法体内部循环次数较多的循环体都可以被称之为热点代码, 因此可以通过JIT编译器编译为本地机器指令,由于这种编译方式发生在执行过程中,因此也被称之为栈上替换, 或者成为OSR (On Stack Replacement) 编译
  • 一个方法究竟要被调用多少次,或者一个循环体究竟需要执行多少次循环才可以达到这个标准? 这必然需要一个准确的阈值,JIT编译器才会将这些热点代码 编译为本地指令执行,这里主要依靠的是热点探测功能
  • 目前HotSpot VM 所采用的热点探测方式是基于计数器的热点探测。
  • 采用基于计数器的热点探测,HotSpot VM 会为每个方法都建立2个不同类型的计数器,分别为方法调用计数器(Invocation Counter) 和 回边计数器(Back Edge Counter)
    • 方法调用计数器用于统计方法的调用次数
    • 回边计数器用于统计循环体执行的循环次数,
  • 这个计数器就用于统计方法被调用的次数,他的默认阈值在Client 模式下是 1500次,在Server模式下是 10000次。 超出这个阈值,就会出发 JIT 编译
  • 这个阈值可以通过虚拟机参数 -XX:CompileThreshold 来人为设定
  • 当一个方法被调用,会先检查该方法是否存在JIT编译过的版本,如果存在,则优先执行编译后的本地代码,如果不存在已经被编译过的版本,则将此方法使的调用计数器值 +1 , 然后判断方法调用计数器与回边计数器的之和是否超过方法调用计数器的阈值,如果已经超过阈值,那么将会向JIT编译器提交一个该方法的代码编译请求。
  • 回边计数器:它的作用是统计一个方法中循环替代玛执行的次数,在字节玛重遇到控制流向后跳转的指令称之为回边(Back Edge), 显然,建立回边技术统计的目的就是为了出发 OSR 编译

在这里插入图片描述

热度衰减
  • 如果不做任何设置,方法调用计数器统计的并不是方法被调用的绝对次数,而是一个相对的执行频率,即一段时间之内方法被调用的次数,当超过一定的时间限制,如果方法的调用次数仍然不足以让他提交给 JIT 编译器,那么这个方法的调用计数器就会被减少一般,这个过程被称之为方法调用计数器热度的衰减(Counter Decay) ,而这段时间就被称之为此方法统计的半衰周期(Counter Half Life Time)
  • 进行热度衰减的动作是在虚拟机进行垃圾收集时顺便进行的,可以使用虚拟机参数-XX:UseCounterDecay 来关闭热度衰减,让方法计数器统计方法调用的绝对次数,这样只要系统运行时间够长,绝大部分方法都会被编译成本地代码
  • 另外:可以使用 -XX:CounterHanlfLifeTime参数设置半衰周期的时间,单位是秒。
HotSpot JVM的执行方式

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值