JIT编译器记录

计算机不能识别高级语言,只能识别机器语言,也就是俗称的二进制0和1,所以必须要把
高级语言翻译成机器语言,这样计算机才可以执行。
那么为什么java不直接编译成二进制机器码,而是偏要搞个字节码勒?
因为在不同平台如果编译成机器码都是不一样的,如果要实现跨平台,就不能直接生成机器码。
我们编写的java源码,都是先用jvm编译成xx.class的字节码,然后再由各个平台的java虚拟机
将字节码翻译成特定的平台机器码再被运行。
不同平台下jdk编译生成的字节码是一样的,虽然由JVM翻译成的机器码却不一样,但是可以实现跨平台。

编译与解释

编译就是利用编译程序从源语言编写的源程序产生目标程序的过程。
解释则不同,解释性语言的程序不需要编译,省了道工序,解释性语言在运行程序的时候才翻译,比如解释性basic语言,
专门有一个解释器能够直接执行basic程序,每个语句都是执行的时候才翻译。这样解释性语言每执行一次就要翻译一次,
效率比较低

java运行步骤

java文件->编译->.class文件,编译成.class字节码,.class需要jvm解释,然后解释执行。
Java很特殊,Java程序需要编译但是没有直接编译成机器语言,即二进制语言,而是编译成字节码(.class)
再用解释方式执行。java程序编译以后的class属于中间代码,并不是可执行程序exe,不是二进制文件,
所以在执行的时候需要一个中介来解释中间代码,这既是java解释器,也就是所谓的java虚拟机(JVM),也叫JDK        

什么是JIT

Just In Time 俗称JIT.
Java程序最初是通过解释器进行解释执行的,当虚拟机发现某个方法或代码块运行的特别频繁时,
会把这些代码认定为“热点代码”(Hot Spot Code)。为了提高热点代码的执行效率,在运行时,
虚拟机会把这些代码编译成本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器
(JIT编译器,不是Java虚拟机内必须的部分)
如果我们执行java代码时候,每次都是先解释字节码,然后将字节码转换成机器码,那么执行速度肯定比可执行的二进制机器码慢
所以才有了JIT时编译技术,在运行时JIT会把翻译过的机器码保存起来,已备下次使用,因此从理论上来说,采用该JIT技术可以,可以接近以前纯编译技术,使用该技术可以加快java程序的执行。

什么情况下JIT起作用

编译对象

被多次调用的方法:
方法调用触发的编译,因此编译器会以整个方法作为编译对象,即标准的JIT编译方式
被多次执行的循环体:
循环体触发的编译动作,但编译器依然按照整个方法(而不是单独的循环体)作为编译对象

触发条件

判断一段代码是不是热点代码,是不是需要触发JIT编译,这样的行为称为:热点探测(Hot Spot Detection),
基于计数器的热点探测:
虚拟机会为每个方法(或每个代码块)建立计数器,统计执行次数,如果超过阀值那么就是热点代码。
缺点是维护计数器开销。

HotSpot

HotSpot每个方法准备了两类计数器:方法调用计数器(Invocation Counter)和回边计数器(Back Edge Counter)
方法计数器
    默认阀值,在Client模式下是1500次,Server是10000次,可以通过参数“-XX:CompileThreshold”来设定
当一个方法被调用时会首先检查是否存在被JIT编译过得版本,如果存在则使用此本地代码来执行;如果不存在,则将方法计数器+1,然后判断“方法计数器和回边计数器之和”是否超过阀值,如果是则会向编译器提交一个方法编译请求
默认情况下,执行引擎并不会同步等待上面的编译完成,而是会继续解释执行。当编译完成后,此方法的调用入口地址会被系统自动改写为新的本地代码地址
还有一点,热度是会衰减的,也就是说不是仅仅+,也会-,热度衰减动作是在虚拟机的GC执行时顺便进行的
回边计数器
    回边,顾名思义,只有执行到大括号”}”时才算+1
默认阀值,Client下13995,Server下10700
它的调用逻辑和方法计数器差不多,只不过遇到回边指令时+1、超过阀值时会提交OSR编译请求以及这里没有热度衰减