JIT即时编译

1、Java执行过程


 

     Java文件通过javac静态编译为class文件

     class文件有2种方式执行:解释执行、JIT即时编译后执行

 

     通常情况下 默认解释器边解释边运行,但对于一些热点代码会首先编译为机器码,缓存起来,后续执行执行缓存的机器码即可,提升执行效率

 

 

 

2、热点代码


 

     Java采用计数器方式确认是否是“热点”代码

     a. 方法调用次数 (方法调用计数器)

         一段时间内的执行次数,当超过一定的时间限度,若还是没有达到阈值,那么它的计数器会减少一半,此过程被称为热度衰减。这段时间称为“半衰周期”

         Client 模式下默认阈值是 1500 次,

         Server 模式下是 10000次,

         这个阈值可以通过 -XX:CompileThreadhold 来人为设定

         半衰周期通过-XX:CounterHalfLifeTime设定(单位秒,默认?),-XX:-UseCounterDecay可关闭半衰周期

 

     b. 方法体内某个循环次数 (回边计数器),

          这种情况也叫栈上替换(OSR编译),它没有半衰周期,因为就在一次方法调用中。

          -XX:OnStackReplacePercentage 来间接调整阈值

               在 Client 模式下, 公式为 方法调用计数器阈值(CompileThreshold)X OSR 比率(OnStackReplacePercentage)/ 100 。

                              其中 OSR 比率默认为 933,那么,回边计数器的阈值为 13995

               在 Server 模式下,公式为 方法调用计数器阈值(Compile Threashold)X (OSR (OnStackReplacePercentage)- 解释器监控比率 (InterpreterProfilePercent))/100
                             其中 onStackReplacePercentage 默认值为 140,InterpreterProfilePercentage 默认值为 33,如果都取默认值,那么 Server 模式虚拟机回边计数器阈值为 10700

 

 

方法计数器:

 

 

回边计数器:

 

 

3、JIT编译优化


JIT编译优化

a. 公共子表达式消除

    例:int d = ( c * b ) * 12 + a + ( a + b * c);
    当这段代码经过虚拟机即时编译器后,他将进行如下优化:减少了栈上操作。
       int d = E * 12 + a + ( a + E);
      有肯还会进行另一种优化:代数化简
        int d = E * 13 + a * 2;

b.  数组边界检查消除

     如在数组访问发生在循环之中,如果编译期只要通过数据流分析可以判断循环变量的取值范围永远在[0,foo.length)中,那在整个循环中就可以把数组的上下界检查消除,这可以节省很多次的条件判断

 

c. 方法内联

   

 

 

 d. 逃逸分析 带来的栈上替换、标量替换、锁消除

 

posted @ 2020-07-12 20:16  蓝天随笔  阅读(260)  评论(0编辑  收藏  举报