一 执行引擎

image.png

1. 概述

  • 执行引擎时Java虚拟机核心组成部分之一,主要做翻译工作

  • “虚拟机”是一个相对于“物理机”的概念,两种都具有执行代码的能力

  • 区别:
    • 物理机执行引擎直接建立在处理器、缓存、指令集和操作系统层面上。
    • 虚拟机执行引擎是由软件自行实现的,因此可以不受物理条件制约定定制指令集与执行引擎的结构体系,能够执行那些不被硬件直接支持的指令集格式。

JVM的主要任务是负责装载字节码到其内部,但并不能执行运行在操作系统上。如果要运行java程序,执行引擎的任务就是将字节码指令解释/编译为对应平台的本地机器指令才可以。

2. 工作过程

  • 执行引擎在执行过程中,依赖于程序计数器来确定执行什么样的字节码指令
  • 通过对象访问定位,访问对象的信息
  • 所有的Java虚拟机的执行引擎输入、输出都是一致的:输入的是字节码二进制流,处 理过程是字节码解析执行的等效过程,输出的是执行结果

即时编译器

3. Java代码编译和执行过程

java代码编译和执行过程.png

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

  • 解释器(边翻译,边执行)

当Java虚拟机启动时会根据定义的规范对字节码采用逐行解释的方式执行,对每行字节码“翻译”为本地机器指令执行

  • JIT(Just In Time Compiler) 编译器(全部翻译,后执行)

就是虚拟机将源代码直接编译成本地机器平台相关的机器语言

为什么说Java时半编译半解释型语言?
在java1.0的时候,java还是解释型语言。后来加入了JIT编译器,二者结合起来执行

4. 为什么解释器与JIT编译器要并存

  • 程序启动后,解释器可以马上发挥作用,减少响应时间
  • 随着程序运行时间的推移,即时编译器发挥作用,把越来越多的热点代码编译成本地代码,获得更高的执行效率
  • 同时,编译器进行激进优化不成立的时候,可以切换为解释器执行

5. 热点代码探测方式

Ⅰ 什么是热点代码?

一个被多次调用的方法,或者循环多次的循环体都可以被称为“热点代码”。因此都可以通过即时编译器编译为本地代码,由于这种编译方式发生在方法执行过程中,被称为栈上替换,或简称OSR(On Stack Replacement)编译

Ⅱ 探测方式

  • 方法调用计数器
  • 回边计数器

    Ⅲ 热度衰减

    如果不做任何设置,在一段时间内热度没有达到阈值,计数器的值就会减少一半,这段时间就叫半衰周期

  • -XX:-UseCounterDecay

关闭热度衰减

  • -XX:CounterHalfLifeTime

设置半衰周期,单位是秒

6. 切换执行方式

  • -Xint

解释器模式

  • -Xcomp

即使编译器模式

  • -Xmixed

混合模式(默认)

7. 编译器分类

HotSpot 中有两个JIT编译器:Client、Server,大多数时候称为:C1编译器、C2编译器
可通过下面两种命令选择,x64下默认使用server

  • -client
  • -server

C1编译器:对字节码进行简单可靠的优化,耗时短
C2编译器:对字节码进行耗时较长的优化,以及激进优化

优化策略

  • C1

    • 方法内联:把目标方法的代码原封不动地“复制”到发起调用的方法之中,避免发生真实的方法调用
    • 去虚拟化:对唯一的实现进行内联
    • 余消除:在运行期间把一些不会执行的代码折叠掉
  • C2

    • 标量替换:用标量值代替聚合对象的属性
    • 栈上分配:对于未逃逸的对象分配对象在栈,而不是堆
    • 同步消除:消除同步操作,通常指synchronized

8. 分层编译策略

程序解释执行时,即可以不开启性能监控触发C1编译器,也可以开启性能监控触发C2编译器。这两种编译器相互协作共同来执行编译任务,就叫分层编译策略

9. 展望:Graal编译器和AOT编译器

Graal编译器:从jdk10加入了的全新的即时编译器,目前还在实验阶段
AOT编译器jaotc:静态提前编译器,在程序运行之前进行编译

AOT编译器
优点:
不必等待即时编译器的预热,减少java应用给人带来“第一次运行慢”的不良体验
缺点:
破坏了“一次编译,到处运行”
降低了java链接过程的动态性,加载的代码在编译期就必须全部已知