JVM 执行引擎简要介绍 - 编译器、解释器

前言

执行引擎讲是 JVM 运行原理的最后一个部分。

相对于类加载机制、运行时数据区,这部分没有那么多的知识点,似乎也没见谁说面试问了这个。

所以本文将会比较简要得描述工作流程,个人认为不重要的就不写了。

JVM 执行引擎简要介绍 - 编译器、解释器_第1张图片


执行引擎概述

对 JVM 而言,执行引擎就是执行代码的一个软件,所以可以不受硬件环境的约束,执行不被操作系统识别的指令集格式。

JVM 的主要任务是负责装载字节码到其内部,但字节码并不能够直接运行在操作系统之上,因为字节码指令并非等价于本地机器指令,它内部包含的仅仅只是一些能够被 JVM 所识别的字节码指令、符号表,以及其他辅助信息。

所以,执行引擎的作用就是:将字节码指令 解释 / 编译 为对应平台上的本地机器指令

简单来说,JVM 中的执行引擎充当了将高级语言翻译为机器语言的译者。

JVM 执行引擎简要介绍 - 编译器、解释器_第2张图片


解释器和编译器

JVM 执行引擎简要介绍 - 编译器、解释器_第3张图片

Java 既有解释器也有编译器:

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

所以 Java 又成为半编译型半解释型语言。

我们一般说的编译执行,所说的编译都是 javac 前端编译,将 .java 文件编译成 .class 文件。

解释器将根据 PC 寄存器的地址逐行解释执行字节码。


解释器

旧版本的解释执行器是 字节码解释器 ,就是逐条翻译,效率低下。现在已经抛弃。

新版本的是模板解释器,模板解释器将每一条字节码和一个模板函数相关联,模板函数中直接产生这条字节码执行时的机器码,从而很大程度上提高了解释器的性能。

在 HotSpot VM 中,解释器主要由 Interpreter 模块和Code模块构成:

  • Interpreter 模块: 实现了解释器的核心功能

  • Code 模块: 用于管理 HotSpot VM 在运行时生成的本地机器指令


JIT 编译器

当 Hotspot JVM 启动时,顺序应当如下:

  1. 解释器首先发挥作用,开始解释执行工作;
  2. 即时编译器逐渐编译字节码,慢慢开始工作;
  3. 即时编译器探寻热点代码,将有价值的字节码再编译成本地机器指令,以换取更高的执行效率。

热点代码编译

热点代码:一个被多次调用的方法,或者是一个方法体内部循环次数较多的循环体。

因此都可以通过 JIT 编译器编译为本地机器指令。

由于这种编译方式发生在方法的执行过程中,因此也被称之为栈上替换,或简称为OSR (On Stack Replacement)编译。


判定一段代码是否是热点代码,需要依靠热点探测功能,它的工作流程如下:

  • 目前 HotSpot VM 所采用的热点探测方式是 基于计数器 的热点探测。

  • HotSpot VM 将会为每一个方法都建立2个不同类型的计数器,分别为方法调用计数器(Invocation Counter) 和回边计数器(Back Edge Counter) 。

    • 方法调用计数器用于统计方法的调用次数;可以通过 -XX:CompileThreshold 进行设置,server 模式(Java默认模式就是 Server,这个就不管了)下默认值为 10000 次。超过这个阈值就会触发 JIT 编译。
    • 回边计数器用于统计循环体执行的循环次数。工作模式和方法调用计数器相同。需要注意的是,它的热点阈值判断需要加上方法调用计数值。

JIT 动态编译判断流程如下:

JVM 执行引擎简要介绍 - 编译器、解释器_第4张图片


热度衰减

当然,如果服务一直运行,一段代码肯定会达到阈值进而判定为热点代码,这是迟早的问题。

这显然是不合适的。

所以执行引擎设置了一个热度衰减机制,这个机制参照半衰期来制定:

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

这个机制嘛,很好理解,了解下把。。。感觉也用不到。。。


执行引擎参数设置

默认情况下,执行引擎都是采用解释器和编译器并存的模式。当然,可以设置:

  • -Xint :完全采用解释器模式执行程序。
  • -Xcomp :完全采用即时编译器模式执行程序。如果即时编译出现问题,解释器会介入执行。
  • -Xmixed :采用解释器+即时编译器的混合模式共同执行程序。

参考文章

https://www.bilibili.com/video/BV1PJ411n7xZ?p=110

https://www.cnblogs.com/yanl55555/p/13334713.html

https://www.cnblogs.com/yanl55555/p/13334713.html

你可能感兴趣的:(#,Java,虚拟机,1024程序员节,编译器,java,jvm)