谈一谈 JIT

前言

今天在看 《深入理解JVM》的时候,有这样一段内容
执行引擎在执行字节码指令的时候,通常会有解释执行和编译执行俩种选择,也可能俩者兼备。对解释执行和编译执行的理解感觉欠缺所以记录下了如下内容

编译执行与解释执行


编译执行

源代码 编译成 本地机器码,然后执行。典型的 C 和 C++ 程序采取这样的方式执行。

优点

但是运行时,由于直接执行本地机器码所以执行很快。

缺点

编译执行在编译的过程比较慢,大型的 C和 C++程序 编译时会非常慢。而且基于寄存器架构,可移植性很差


解释执行

在每条代码执行的时候,将代码解释为机器码执行,每条代码被重复解释。

优点

可移植性比较好,不依赖于PC的 寄存器 等 ,JAVA源文件被 java编译器编译为 不依赖于任何操作系统的字节码文件(受JVM和java虚拟机规范约束)成为 java实现跨平台的关键

缺点

翻译一条执行一条,效率低,耗时长。这也是早期JAVA受到诟病的主要原因。


AOT和 JIT
JIT

just in time compilation,即时编译。在运行时进行编译

AOT

ahead of time Compilation ,事前编译。在执行代码前进行编译


概述 JIT
类从加载到创建对象执行方法的过程

java 源文件 经 javac编译器编译 成为 字节码文件,字节码文件受 java虚拟机的规范的约束,有着严格的数据格式。当Java 程序执行时需要用到该类时,java 类加载器会根据类的全限定名将 class 文件的二进制字节流加载进内存然后经过 验证,准备 ,解析,初始化阶段 正式 成为可用的 java的类型。需要注意的是,在这个过程种 我们所写的源代码中的 方法体中的方法 被javac编译成为 字节码指令,在class文件加载进内存后,这部分被存放在方法区中。然后由 执行引擎 将 字节码指令 解释或编译为 本地机器上的机器指令 然后 由 CPU执行。

执行引擎解释或者翻译字节码指令

1,在部分的商用虚拟机例如 hotSpot,执行引擎采取的是 编译执行+翻译执行的方式。Java程序最初通过解释器来执行字节码指令,当发现一个方法或者循环体多次被执行时,就会将个方法或者循环体定为是 “热点代码”,为了提高这热点代码的执行效率,JIT编译器将 他们编译为 本地机器码,并进行各种层次的优化。
2, 即时编译器并不是虚拟机必须的部分,Java虚拟机规范并没有规定Java虚拟机内必须要有即时编译器存在,更没有限定或指导即时编译器应该如何去实现。但是,即时编译器编译性能的好坏、代码优化程度的高低却是衡量一款商用虚拟机优秀与否的最关键的指标之一,它也是虚拟机中最核心且最能体现虚拟机技术水平的部分。

HotSpot 虚拟机使用 解释器和 编译器并存的架构

当虚拟机启动时,解释器马上发挥作用,不必等待编译器编译的过程,省去了等待编译的时间。随着程序的运行,编译器发挥作用,将热点代码编译为本地机器码,加快执行效率

在 HotSpot中 JIT 编译器的分类

通过 java - version 指令 可以得到 jvm运行模式

  1. client :jvm运行在client模式下,使用 C1 轻量级的编译器 。主要在客户端使用。C1编译器会对字节码进行简单的优化,耗时短,目的在于 获得更快的执行速度 。运行在client 模式下的java程序启动很快
  2. server:JVM运行在 server 模式下,使用 C2重量级的 编译器。主要在 服务器端使用。C2编译器会对字节码进行耗时长,深层次的优化,比较耗时。但是编译完成后,代码执行效率比较高,适用于 服务器端这类不太注重 启动效率而关注执行效率的程序。
编译的时间开销及空间开销
  1. 编译的时间开销主要就是编译为机器码的过程比较耗时。但是编译结束后执行效率是杠杠的
  2. 空间开销,编译后的代码的大小比较未编译前的字节码的大小,膨胀10X非常正常,所以只有热点代码才值得被编译
判定热点代码的方法
  1. 基于热点采样的热点探测
    周期性的检查各个线程的虚拟机栈的栈顶,如果发现某些方法经常出现在栈顶,那么这些方法就是热点代码。这种探测方法简单高效,但是探测过程中容易收到线程阻塞或者别的外界因素的影响。
  2. 基于计数器的热点探测
    使用计数器来对方法调用次数进行统计,当计数器达到一定的阈值的时候,认定该方法为热点代码,这种探测方式实现相对的麻烦,需要维护计数器,但是统计结果更加的准确。
HotSpot采取的热点探测的方法

HotSpot采用的基于计数器的热点探测方法,他为每一个方法维护俩个计数器,一个是方法调用计数器,一个是回边计数器

  1. 方法调用计数器
    用于统计的方法的调用次数,默认阈值在Client模式下为 1500次,在Servler模式下是10000 次。当一个方法被调用时,首先检查该方法是否存在被 JIT编译后的版本,如果存在则执行 该版本。如果不存在那么将方法的方法计数器值加一,然后判断方法计数器和回边计数器的和是否达到阈值,如果达到,向JIT编译器提交编译该方法的编译请求,若不做任何设置,执行引擎不会等待方法编译完成,而是以解释执行的方法执行方法。在不做任何设置的情况下,统计的方法调用次数是相对次数(该方法在一定时间内被调用的次数),如果在这段时间内方法调用次数还达不到需要JIT编译的要求,方法调用次数减半,这称之为 热度衰减。这段时间称之为半衰周期。

  2. 回边计数器
    用于统计 循环 体的执行次数,在字节码中遇到控制流向后跳转的指令称为“回边”。

不求巨变,只求每天一点点蜕变

参考 :
https://blog.csdn.net/Jhno99/article/details/106862319
https://www.cnblogs.com/dzhou/p/9549839.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值