jvm 解释器和编译器

在商用虚拟机中,Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或者代码块运行特别频繁时 就会把这些代码认定为“热点代码”,为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码 ,完成这个任务的编译器成为即时编译器(JIT Just In Time Compiler)

解释器

当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行。

编译器

程序运行后随着时间的推移,编译器逐步发挥作用,把越来越多的代码编译成本地代码之后,可以获得更高的执行效率。

编辑的“热点代码” 有以下两类:

  • 被多次调用的方法
  • 被多次执行的循环体

第一种是由于方法被调用触发的编译,编译器理所当然以整个方法作为编译对象,这种编译也是虚拟机中标准的JIT编译方式。对于后一种情况 ,尽管是循环体,但是编译器依然会以整个方法作为编译对象,而不是单独的循环体。这种逼啊安逸方式因为编译放生在方法执行过程之中,因 此被形象的成为"栈上替换"(On Stack Replacement ,简称OSR编译,即方法栈帧还在栈上,方法就被替换了)

还有一个问题是什么叫做多次,多次是指多少次?还有一个问题就是虚拟机如何统计一个方法执行的次数?解决了这个问题也就回答了即时编译 触发的条件。
判断一段代码是不是热点代码,是不是需要触发即时编译,这样的行为成为热点探测 ,其实热点探测并不需要一定要知道方法具体被调用 了多少次,目前主要的热点探测的方式有如下两种:

  • 基于采样的热点探测:这种方式虚拟机会周期性的检查各个线程的栈顶,如果发现某个方法经常出现在栈顶,这个方法就是“热点方法”。采 样的方法简单,高效,并且还能比较容易的获取调用链,缺点是很难比较有精度的确认一个方法的精度,容易受到线程阻塞或者别的外界因素影 响。
  • 基于计数的热点探测:这种方法虚拟机会为每个方法(甚至是代码块)建立计数器,统计方法的执行次数,如果执行次数超过一定的阈值就会 被认定为“热点方法”。这种统计方式比较麻烦,需要为每个方法维护一个计数器,但是比较精准。
    HotSpot虚拟机采用的是第二种,基于计数器热点探测方式。因此它为每个方法准备了两类计数器,方法调用计数器和回边计数器。

方法调用计数器,顾名思义,就是统计方法调用的次数,在client模式下,是1500次,在server模式下是10000次。这个阈值可以通过虚拟 机参数-XX::CompileThreadhold来人为设定。方法调用时,先检查是否存在被JIT编译后的版本,存在的话则优先使用编译后的本地代码, 不存在则将此方法计数器+1,然后判断方法调用计数器和回边计数器纸盒是否超过方法调用计数器的阈值。超过的话就发送及时编译请求。

这个次数不是绝对的次数,而是一个相对的执行频率,即一段时间之内方法被调用的次数,当超过一定的时间限度,如果方法的调用次数仍然不足 以让他提交给即时编译器编译,那这个方法的调用计数器会被减少一半,这个过程被成为方法调用计数器热度衰减,而这段周期就称为半衰周期。 进行热度衰减的动作是在垃圾回收时顺便进行的,可以使用虚拟机参数 -XX:-UseCounterDecay来关闭热度衰减,可以使用 -XX:CounterHalfLifeTime 参数设置半衰周期的时间,单位是秒。 另外一种计数器——“回边计数器”,它的作用是统计方法中循环体代码的执行次数,在字节码中遇到控制流向后跳转的指令成为“回边”。目的 也是为了触发OSR编译。与方法计数器不同,回边计数器没有计算热度衰减的过程,因此这个计数器就是该方法循环的绝对次数

猜你喜欢

转载自my.oschina.net/u/2457218/blog/1793115