JVM 执行引擎

HopSpot虚拟机的执行引擎发分为:解释器、JIT编译器、GC组成

  • 解释器
    • 解释执行代码,对字节码进行逐行解释的方式,将每一条字节码文件中的内容"翻译"成机器指令去执行
  • JIT编译器
    • 把热点代码编译成机器指令缓存到元空间(方法区)
    • 注意和前端编译器(即把文件编译成class文件的编译器)区分

JVM 执行引擎_第1张图片

JVM 的执行方式

        启动时先使用解释器执行,不需要等待JIT编译器全部编译完成再执行。程序的执行过程中根据方法的执行次数循环体的回边次数决定热点代码(热点探测),将其编译为本地机器指令(不同平台的机器指令是不同的)
JVM 执行引擎_第2张图片

JIT编译器

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

        如何确定热点代码:HopSpot热点探测方式是基于计数器热点探测,HopSpot会为每个方法建立两个不同的计数器,分别是:方法调用计数器、回边计数器。分别统计方法调用次数和循环体的循环次数。

        方法调用计数器:在Client模式下的默认阈值是1500次,在Server模式下的默认阈值是10000次,可以通过虚拟机参数:-XX:CompileThreshold来人为的设定

 JVM 执行引擎_第3张图片

         当一个方法被调用的时候,会先检查该方法是否存在被JIT编译过的版本,如果存在,优先使用编译后的本地代码来执行。如果不存在,则方法调用计数器值加1,然后判断方法调用计数器与回边计数器之和是否大于阈值。如果已超过阈值,那么会向即时编译器提交一个该方法的编译请求。(本次还是解释执行)

        方法调用计数器数值是不是只增不减?不是的。

热度衰减

        方法调用计数器并不是一个方法被调用的绝对次数,而是一个相对的频率,即在一段时间内方法被调用的次数。如果在一段时间内该方法的调用计数器仍然不足以满足提交JIT编译的次数(即没有达到阈值),那么该方法的调用计数器的次数将减半。这个过程就叫方法调用计数器热度的衰减。这段时间就称做该方法统计的半衰周期。
        进行热点衰减的动作是在虚拟机进行垃圾收集时顺带进行的。可以使用虚拟机参数:-XX:-UseCounterDecay来关闭热点衰减。让方法计数器统计的是绝对的调用次数。这样只要程序运行的时间足够长,绝大部分的方法都会被编译成本地代码
        可以使用-XX:CounterHalfLifeTime 参数设置半衰周期的时间,单位是秒

回边计数器 JVM 执行引擎_第4张图片

HopSpot设置程序执行方式

        HopSpot默认的情况下是使用解释器和编译器并存的架构,也可以显示的指定是完全采用解释器还是编译器执行

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

JIT的分类

  • -client:指定Java虚拟机运行在client模式下,使用C1(Client Compiler)编辑器
    • C1编辑器对字节码进行简单和可靠的优化,耗时短,以达到更快的编译速度
  • -server:指定Java虚拟机运行在server模式下,使用C1(Server Compiler)编辑器
    • C2进行耗时长的优化,以及激进的优化。但是优化的代码执行效率更高。

C1和C2不同的优化策略

JVM 执行引擎_第5张图片

AOT编译器(静态提前编译器)

        JVM 执行引擎_第6张图片

  • 好处
    • Java虚拟机加载已经预编译的二进制库,可以直接执行。不需要等待即时编译器的预热。减少了Java应用给人带来“第一次运行慢”的体验
  • 缺点
    • 破坏了Java的“一次编译,到处运行”,必须为每一个不同的硬件、os编译对应的发行包
    • 降低了Java链接过程的动态性,加载的代码在编译期就必须全部已知
    • 最初只支持Linux x64   java base

你可能感兴趣的:(JVM,java)