JVM——执行引擎详解,接地气总结

JVM主要负责的任务就是装载字节码,但是字节码不能直接在操作系统之上运行,字节码并非是本地指令,它包含的指令仅可以被JVM所解析。所以执行引擎的任务就是将字节码指令解释/编译为对应平台上的本地机器指令后再做执行,充当了高级语言到机器指令的翻译角色

Hotspot VM包括解释器和JIT即时编译器(两者共用,互相弥补)

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

最初Java就只有解释执行,发展到后来JIT即时编译器的加入,将Java变成了半编译半解释型语言。目前Java借助解释器和JIT编译器,并在多年发展进化之后,Java程序的运行已经摆脱了早期性能差的情况,相当接近C/C++。

解释器

将每条字节码文件中的内容翻译为对应平台的本地机器指令,当一条指令解释执行结束后,就回去PC寄存器中获取下一条需要执行的指令符号索引,执行下一条字节码指令。

Java的发展过程中,出现过两种解释器:

  • 早期的字节码解释器
    • 在执行的时候通过软件直接的对字节码进行解析执行,效率低
  • 目前普遍使用的模板解释器
    • 将每一条字节码和一个模板函数相关联,模板函数中直接生成这条字节码指令执行时的本地机器指令,提高了性能,

但是解释器的效率相较于C/C++的高级语言->汇编->机器码的过程还是有明显差距的。为了提高执行效率,JIT即时编译器就被JVM所支持。

JIT即时编译器

概念解释:

  • 前端编译器:把Java文件编译成为class字节码文件
    • 类加载子系统
  • 后端运行期编译器(Just In Time Compiler):把字节码指令转换为机器码的过程
    • JIT编译器
  • 静态编译器(Ahead Of Time Compiler):直接把java文件编译成本地机器码

即时编译器的目的是为了避免函数被解释执行,而是将整个函数体编译成为机器码,每次函数执行的时候,只执行编译后的机器码就好了。直接执行机器码就提高了执行的效率。

既然JIT编译器在效率上的优势这么明显,为什么还保留了解释器呢?

当程序启动之后,解释器就可以马上发挥作用,省去了编译的时间而编译器要想发挥其优势,需要一定的时间将代码变成本地机器码,一旦这个过程完成,那么程序的执行效率就将大大提高。与之一致的就是Hotspot VM,它就是使用了解释器和JIT编译器相弥补的方式。

号称执行效率最快的JRockit VM就没有解释器,由于缺少解释器的,所以其在程序启动之后的响应上必然就要慢一些。然而对于服务端的应用来说,这一点是可以接受的,然而对于那些看重启动时间的应用来说,可能解释器和JIT编译器共存的模式是一种最为平衡的模式

需要注意的是:

针对于解释器和JIT编译器共存模式的JVM来说,当一个热机状态下承载的流量发向一个刚启动的冷机应用程序的时候,可能会由于JIT编译器还没有将字节码指令完全的解析为本地机器指令的时程序的指令过慢,导致这台冷机宕机

热点代码及探测

什么时候用JIT编译器呢?

需要根据代码被调用执行的频率而定,对于那些需要被后端编译成为本地机器码的字节码指令,称之为“热点代码”,JIT编译器在运行时会针对那些频繁被调用的热点代码做出深度优化,将其直接编译为本地机器码,以提升性能。

一个被多次调用方法,或者是一个方法体内循环次数较多的循环体都有可能被视为热点代码,JIT会将这些代码翻译为机器指令,由于这种后端编译发生在程序的执行过程中,因此也被成为“栈上替换”,简称OSR(On Stack Replacement)

一个方法被调用多少次,或者一个循环体要执行多少次循环才会被视为热点代码呢?

热点代码的发现是由热点探测功能来决定的,目前Hotspot VM所采用的热点探测方式是基于计数器的热点探测:Hotspot VM将会为每一个方法都建立2个不同类型的计数器,分别为方法调用计数器回边计数器

  • 方法调用计数器:用于统计方法的调用次数
    • 默认阈值在client模式下是1500次,在server模式下是10000次。超过阈值将会触发JIT编译器,并且缓存编译后的机器指令到方法区(元空间)
    • 这个阈值可以通过参数-XX:CompilerThreshold设定
    • 热度衰减
      • 默认情况下,方法调用计数器统计的并不是方法被调用的绝对次数,而是某一时间段内方法被调用的次数。当超过一定时间限度,方法的调用次数仍然不足以让它提交给即时编译器编译,那这些方法的调用计数器次数将减半,这个过程称之为衰减(Counter Decay),而这段时间就称为此方法统计的半衰周期(Counter Half Life Time)
      • 热度衰减的执行是在进行垃圾回收的时候顺便执行的,可以使用-XX:-UseCounterDecay来关闭热度衰减,让方法计数器统计方法调用的绝对次数。当关闭热度衰减之后,只要程序运行的时间足够的长,那么绝大部分的代码都将被编译成本地机器码
      • 通过-XX:CounterHalfLifeTime参数设置半衰周期的时间,单位秒
  • 回边计数器:用于统计循环体执行的循环次数
    • 当回边计数器统计的次数和该循环所在方法的方法调用次数相加到达阈值之后,触发即时编译器

Hotspot VM设置程序执行方式

默认情况下Hotspot VM采用的是解释器和编译器并存的方式

可以显式的设置VM的执行方式是完全采用解释器,还是完全采用即使编译器。

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


在Hotspot VM中内置有两个JIT编译器,分别为Client Compiler和Server Compiler,简称C1编译器和C2编译器,可以显式的指定JVM使用哪种编译器:

  • -client:指定JVM运行在Client模式下并使用C1编译器
    • C1编译器会对字节码进行简单和可靠的优化,耗时短,以达到更快的编译速度
    • 优化策略
      • 方法内联:将应用的函数代码编译到引用点处,这样可以减少栈帧的生成,减少参数传递以及跳转过程
      • 去虚拟化:对唯一的实现类进行内联
      • 冗余消除:在运行期间把一些不会执行的代码折叠掉
  • -server:指定JVM运行在Server模式下,并使用C2编译器(C++编写)
    • C2进行耗时较长的优化(后端编译),以达到最极致的优化,代码的执行效率更高
    • 优化策略(优化层面在于全局层面,逃逸分析是优化的基础,基于逃逸分析在C2上进行优化)
      • 标量替换:用标量代替聚合对象的属性值
      • 栈上分配:对于为逃逸的对象分配对象在栈而不是堆
      • 同步消除:消除不必要的同步操作,通常是指消除synchronized

说白了就是C1编译器优化的很柔和,而C2编译器更彻底一些。

如果JVM所在机器是64位的,那么JVM只支持Server VM

64位版本的Java SE Development Kit (JDK)目前忽略-client选项,而是使用Server VM

说白了就是64位的JDK,就老实用Server VM,别改了。


那么64位的情况下就没有办法使用C1了嘛?

分层编译(Tiered Compilation)策略:程序解释执行(不开启性能监控)可以触发C1编译,将字节码编译成本地机器码,可以进行简单优化。也可以加上性能监控,C2编译触发,会根据性能监控信息进行激进的优化。

Java7之后,一旦在程序中显式的指定 -server 的时候,默认将会开启分层编译策略,由C1和C2相互协作共同来执行编译任务。

拓展

  • JDK10开始,Hotspot VM又加入了一个新的即时编译器——Graal编译器。其效率已经追平了C2编译器(与C1和C2是同一级别),目前其处于实验状态,通过-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler去开启使用。
  • AOT编译器(静态提前编译器,Ahead Of Time Compiler):JDK9引入,AOT编译工具jaotc(将class文件编译成.so文件),借助Graal编译器,将所输入的Java类文件转换为机器码,并存放至生成动态共享库中。
    • 所谓AOT编译,其实就是与即时编译相对立的一个概念。即时编译是在程序的运行过程中将字节码转换为可在硬件上直接执行的机器码,并部署至托管环境中的过程;而AOT编译就是在程序执行之前就将字节码转换为本地机器码的过程
    • 优点:在程序运行之前就完成了对字节码指令的后端编译,JVM加载已经预编译成二进制库,直接运行,减轻了Java应用所谓的“第一次运行慢的”问题
    • 缺点
      • 破坏了Java 一次编译,多平台执行的特点(class文件可以多平台执行,只要有各个平台的JVM就行了),必须为每个不同的平台和硬件编译其对应的发行包。
      • 降低了Java链接过程的动态性,加载的代码在前端编译期就必须全部已知。
      • 还不成熟,目前仅支持Linux x64

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值