21.执行引擎概述

执行引擎概述

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

执行引擎的工作流程

  • 执行引擎在执行的过程中究竟需要执行什么样的字节码指令完全依赖于PC寄存器
  • 每当执行完一项指令操作后,PC寄存器就会更新下一条需要被执行的指令地址
  • 当方法在执行过程中,执行引擎有可能会通过存储在局部变量表中的对象引用准确定位到存储区在java堆区中的对象实例信息,以及通过对象头中的元数据指针定位到目标对象类型信息

image-20200710081627217

Java代码编译和执行过程

image-20200710082141643

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

  • 前面橙色是生成字节码文件的过程 和JVM无关
  • 后面蓝色和绿色是JVM需要考虑的过程

java代码编译是由java源码编译器来完成

image-20200710082433146

Java字节码的执行就由JVM执行引擎来完成

image-20200710083036258

总结 解释器和编译器

image-20200710083656277

解释器 Interpreter

当JVM启动会根据预先定义的规范堆字节码采用逐行解释的方式执行,将每条字节码文件中的内容翻译为对应平台的本地机器指令执行

JIT编译器

JIT(Just In Time)编译器,即时编译器,虚拟机将源代码直接编译成和本地机器平台相关的机器语言

java是半编译半解释性语言

JDK1.0时,java是解释性语言,随着java的发展也可以直接生成本地代码编译器,现在JVM在执行java代码的时候,通常会将解释执行与编译执行二者结合起来.翻译成本地代码后,就可以做一个缓存操作,存储在方法区中,这就是方法中存储的JIT缓存

机器码 指令 汇编语言

  • 机器码 01组成的二进制编码的指令,就成机器指令.CPU直接运行,与CPU紧密相关不同的CPU,机器指令也不同,可读性差

  • 指令 特定的01二进制序列组成的指令 一般是英文简写如mov inc等 可读性稍好,不同硬件平台同一个操作,对应的机器码可能不同,所以同一条指令在不同的硬件平台下对应的机器码也会不同

  • 指令集 不同硬件平台,各自支持的指令有差别,因此每个平台所支持的指令,称为对应平台的指令集

    • x86指令集 对应X86架构平台
    • ARM指令集 对应ARM架构平台
  • 汇编语言 用助记符(Memonics)代替机器指令的操作码,用地址符(Symbol)和标号(Label)来代替指令或操作数的地址.在不同硬件平台,汇编语言对应着不同的机器语言指令集,通过汇编过程转成机器指令

​ 计算机只认机器码,所以用汇编语言编写的程序还必须翻译成机器码,计算机才能识别和执行

  • 高级语言 更接近人的语言,仍然需要将程序解释和编译成机器码,这个过程叫做解释程序或者编译程序

  • image-20200710085323733

字节码

字节码是一个中间状态的二进制代码文件,他比机器码更抽象,需要直译器转移后才能成为机器码,字节码主要为了实现特定软件运行和软件环境与硬件环境无关.字节码的实现方法是通过编译器和虚拟机.编译器将源码编译成字节码文件,特定平台上的虚拟机让字节码文件翻译成可以直接执行的机器码

解释器

JVM设计者初衷为了单纯满足JAVA程序实现跨平台特性,因此避免采用静态编译的方式直接生成本地机器指令,从而诞生了实现解释器在运行时采用逐行解释字节码执行程序的想法

image-20200710090203674

java源文件不直接翻译成机器指令,可能是因为直接翻译的代码量大,工程复杂.解释器承担运行时翻译者的角色,将字节码文件中的内容翻译成对应平台本地机器指令,当一条字节码指令被解释执行完毕之后,接着再根据PC寄存器中记录的下一条需要执行的字节码指令执行解释操作

解释器分类

  • 在java发展历史中,一共有两套解释执行器,古老的字节码解释器和,现代的模板解释器
  • 字节码解释器在执行时通过纯软件代码模拟字节码的执行,效率非常低下
  • 而模板解释器将每一条字节码和一个模板函数相关联,模板函数中直接产生这条字节码执行时的机器码,从而很大程度上提高了解释器的性能
  • 在HotSpotVm中,解释器主要由Interpreter模块和Code模块构成
    • Interpreter模块: 实现了解释器的核心功能
    • Code模块: 用于管理HotSpotVM在运行时生成本地机器指令

JIT编译器

Java代码的执行分类

  • 第一种是将源代码编译成字节码文件,然后在运行时通过解释题将字节码文件转成机器码执行
  • 第二种是编译执行(直接编译成机器码).现在虚拟机为了提高执行效率,会使用即是编译技术JIT将方法编译成机器码再执行
  • HotSpot是目前市面上高性能虚拟机的代表之一.它采用解释器和即时编译器并存的架构.在JVM运行时,解释器和即时编译器能够相互协调,各自取长补短.尽量选择最合适的方法来权衡编译本地代码的事件和直接解释执行的事件

有了JIT为什么还需要解释器?

  • JRockit虚拟机砍掉了解释器,只采用JIT.因为JRockit只部署在服务器上,一般已经有足够的时间让他进行指令编译过程,对于响应要求不高,等编译完成之后又更好的性能
  • 程序启动后,解释器可以马上发挥作用,省去编译时间,立即执行.编译器想要发挥作用.需要把代码编译成本地代码,需要一定执行的时间,但编译成本地代码后,执行效率高
  • 所以JRockitVM执行效率高,但程序在启动的时候需要花费更长的事件来编译.但在服务器端来说.更看重的给用户响应的时间而不是启动时间.
  • 在JVM启动时,解释器立刻发挥作用,随着时间推移,编译器发挥作用,把越来越多的代码编译成本地代码,从而提高执行效率

HotSpot JVM执行方式

  • JVM启动之后,解释器立刻发挥作用,对代码进行解释执行,随着程序运行时间的推移,JIT逐渐发挥作用,根据热点探测功能,将有价值的字节码编译成本地机器执行,从而提高执行效率
  • 案例
    • 机器在热机状态下可能承受的负载要大于冷机状态.如果以热机状态的流量进行切流,可能使得处于冷机状态下的服务器无法承载流程而假死
    • 在生产环境发布过程中,以分批的方式进行发布,根据机器数量划分成多个批次,每个批次的机器数至多占到整个集群的1/8
  • 概念解释
    • .java文件 编译成.class文件的过程叫前端编译,对应前端编译器 也可能是JVM在运行时期编译器JIT生成的class文件
    • 还可能是使用静态提前编译器AOT(Ahead Of Time Compiler)直接把.java文件编译成本地机器代码
    • 前端编译器: Sun的javac Ecplise JDT中的增量式编译器ECJ
    • JIT编译器: HotSpot VM的C1 C2编译器
    • AOT编译器: GNU Compiler for the Java GCJ , Excelsior JET

热点探测技术

  • 一个被多次调用的方法,或者方法体中的循环次数较多的循环体都可以成为热点代码.因此都可以通过JIT编译成本地机器指令,由于这种编译行为发生在方法执行的过程中,因此也成为栈上替换OSR(On Stack Replacement)编译
  • HotSpot VM采用热点探测方式是基于计数器的特点探测
  • 采用基于计数器的热点探测,HotSpot VM将为每一个方法建立2个不同类型的计数器,分别是方法调用计数器(Invocation Counter) 和 回边计数器(Back Edge Counter)
    • 方法调用计数器 记录方法调用的次数
    • 汇编计数器 记录循环体执行的次数

方法调用计数器

  • 用于统计方法的调用次数,默认阈值在Client模式1500次,Server模式下是10000次.超过这个阈值就会触发JIT编译
  • -XX:CompileThreshold 来认为设置这个阈值
  • 当一个方法被调用,会先检查改方法是否存在JIT编译后的版本,如果存在直接使用被JIT编译成本地代码来执行.如果不存在,此方法的调用计数器+1,然后判断方法调用计数器和回边计数器是否超过阈值,如果超过阈值触发JIT提交一个该方法的代码编译请求
  • image-20200710101829934

回边计数器

  • 统计一个方法中循环体的执行次数,在字节码中遇到控制流往后跳转的指令成为回边(Back Edge).
  • 遇到回边指令,先判断JIT中是否有编译好的版本,有直接用,没有回边计数器+1,判断是否超过阈值,超过阈值触发JIT编译成本地代码缓存到方法区
  • image-20200710103103869

热点衰减

  • 不做任何设置,方法调用计数器统计的并不是被方法调用的绝对次数,而是一个相对的执行频率,即一段时间之后方法被调用的次数.当超过一定的时间限定,如果方法调用次数打不到阈值触发JIT,那么这个方法的调用计数器就会减少一半,这个过程称为方法调用技术器的热度衰减,而这段时间称为方法统计的半衰期(Counter Half Life Time)
  • 进行热度衰减的动作是在虚拟机进行垃圾收集时顺便进行的,可以使用-XX:-UseCounterDecay来关闭热度衰减,让方法计数器统计变成绝对次数,这样只要系统运行时间够长,绝大部分方法都会被编译成本地代码
  • -XX:CounterHalfLifeTime 设置半衰期的时间周期,单位是秒

HotSpot 设置程序执行方法

  • 缺省情况下HotSpot VM采用解释器和JIT并存的架构,也可以根据具体应用场景,通过命令显示为java虚拟机执行运行时采用哪种架构
  • -Xint: 完全采用解释器执行程序 int interpret
  • -Xcomp: 完全采用JIT模式执行程序,如果JIT编译出现问题,解释器会接入
  • -Xmixed: 混合使用
  • image-20200710103340273

HotSpot中的JIT分类

  • HotSpot编译器分为两种分别是C1和C2,分别对应Client Compiler和Server Compiler.开发人员可以通过命令显示执行java虚拟机在运行时选用哪一种即时编译器

  • -client: java -client XXXX 指定JVM运行在Client模式下 使用C1编译器

    • C1编译器会对字节码进行简单和可靠的优化,耗时短,以达到更快编译速度
  • -server: 执行JVM运行在Server模式下 默认64位系统就是Server模式 使用C2编译器

    • C2进行耗时较长的优化,以及激进优化,但优化的代码执行效率更高

C1 C2不同的优化策略

  • C1 方法内联 去虚拟化 冗余消除
    • 方法内联: 将引用的函数代码编译到引用点处,减少栈帧的生成,减少参数传递以及跳转过程
    • 去虚拟化: 对唯一的实现类进行内联
    • 冗余消除: 运行期间把一些不会执行的代码折叠
  • C2优化在全局层面,逃逸分析是优化基础,基于逃逸分析在C2上有几种优化
    • 标量替换: 用标量替换对象中的聚合属性
    • 栈上分配: 对于未逃逸的对象分配对象在栈上而不是堆中
    • 同步消除: 锁消除 消除同步操作 synchronized

分层编译策略

  • 分层编译(Tiered Compilation)策略: 程序解释执行(不开启性能监控)可以出发C1编译,将字节码编译成机器码,可以进行简单优化,也可以加上性能监控,C2编译会根据性能监控的信息进行激化优化
  • JDK7之后,一旦开发人员在程序中显示指定命令’-server’,默认会开启分层编译策略,由C1编译器和C2编译器相互协同来变成编译任务

总结

  • 一般来说,JIT编译出来的机器码性能比解释器高
  • C2编译器启动时间比C1慢,系统稳定执行以后,C2编译器执行速度远快于C1编译器

AOT编译器

  • JKD9引入了AOT编译器(静态提前编译器 Ahead of Time Compiler)

  • JDK9引入了实验性的AOT编译器工具aotc.他接触了Graal编译器,将所有输入的java类文件转换成机器码,并存放至生成的动态共享库之中

  • 所谓AOT编译,就与JIT编译相对立的概念.JIT是指程序运行过程中,将字节码转换成可以在硬件上可以直接运行的机器码,并部署至托管环境中,而AOT编译是指在程序运行之前,将字节码转换成机器码

  • .java-> .class -> 使用jaotc -> .so

  • 优点: java虚拟机加载已经预编译成的二进制库,可以直接运行.不必等待JIT预热,减少java应用给人带来的第一次运行慢的体验

  • 缺点

    • 破坏了java 一次编译,到处运行 理念, 必须为每一个不同硬件和OS的编译对应的发行包
    • 降低了java链接过程的动态性,加载的代码在编译期就必须全部已知
    • 还需要继续优化 目前支持 Linux X64 java base

补充

  • 自JDK10起,HotSpot又加入全新的即时编译器: Graal编译器

  • 编译效果短短几年就追评了G2编译器,未来可期

  • 目前还在实验阶段,需要使用开关参数去激活才能使用

  • -XX:+UnlockExperimentalvMOptions -XX:+UseJVMCICompiler
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值