在商用虚拟机中,Java程序最初是经过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或者代码块运行特别频繁时 就会把这些代码认定为“热点代码”,为了提升热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码 ,完成这个任务的编译器成为即时编译器(JIT Just In Time Compiler)线程
当程序须要迅速启动和执行的时候,解释器能够首先发挥做用,省去编译的时间,当即执行。server
程序运行后随着时间的推移,编译器逐步发挥做用,把愈来愈多的代码编译成本地代码以后,能够得到更高的执行效率。对象
编辑的“热点代码” 有如下两类:编译器
第一种是因为方法被调用触发的编译,编译器理所固然以整个方法做为编译对象,这种编译也是虚拟机中标准的JIT编译方式。对于后一种状况 ,尽管是循环体,可是编译器依然会以整个方法做为编译对象,而不是单独的循环体。这种逼啊安逸方式由于编译放生在方法执行过程之中,因 此被形象的成为"栈上替换"(On Stack Replacement ,简称OSR编译,即方法栈帧还在栈上,方法就被替换了)虚拟机
还有一个问题是什么叫作屡次,屡次是指多少次?还有一个问题就是虚拟机如何统计一个方法执行的次数?解决了这个问题也就回答了即时编译 触发的条件。
判断一段代码是否是热点代码,是否是须要触发即时编译,这样的行为成为热点探测 ,其实热点探测并不须要必定要知道方法具体被调用 了多少次,目前主要的热点探测的方式有以下两种:编译
方法调用计数器,顾名思义,就是统计方法调用的次数,在client模式下,是1500次,在server模式下是10000次。这个阈值能够经过虚拟 机参数-XX::CompileThreadhold来人为设定。方法调用时,先检查是否存在被JIT编译后的版本,存在的话则优先使用编译后的本地代码, 不存在则将此方法计数器+1,而后判断方法调用计数器和回边计数器纸盒是否超过方法调用计数器的阈值。超过的话就发送及时编译请求。效率
这个次数不是绝对的次数,而是一个相对的执行频率,即一段时间以内方法被调用的次数,当超过必定的时间限度,若是方法的调用次数仍然不足 以让他提交给即时编译器编译,那这个方法的调用计数器会被减小一半,这个过程被成为方法调用计数器热度衰减,而这段周期就称为半衰周期。 进行热度衰减的动做是在垃圾回收时顺便进行的,可使用虚拟机参数 -XX:-UseCounterDecay来关闭热度衰减,可使用 -XX:CounterHalfLifeTime 参数设置半衰周期的时间,单位是秒。 另一种计数器——“回边计数器”,它的做用是统计方法中循环体代码的执行次数,在字节码中遇到控制流向后跳转的指令成为“回边”。目的 也是为了触发OSR编译。与方法计数器不一样,回边计数器没有计算热度衰减的过程,所以这个计数器就是该方法循环的绝对次数cli