49.执行引擎中的解释器和JIT编译器(热点代码探测)
JVM
的执行引擎采用了解释器和JIT编译器的方式来执行代码。
1.解释器
2.JIT编译器
1.HotSpot
采用解释器与即时编译器并存的架构。
2.为什么有了JIT
编译器还需要解释器?
当JVM
虚拟机启动时,解释器可以首先发挥作用,而不必等待即时编译器全部编译完成之后再执行,这样就可以省去许多不必要的编译时间。随着时间的推移,编译器发挥作用,把越来越多的代码编译成本地代码,获得更高的执行效率。(解释器提高了应用程序的响应时间,JIT
编译器提高了应用程序的运行速度)
3.热点代码探测技术
1.编译器概念Java
的编译器有前端编译器、JIT
编译器、AOT
编译器。
前端编译器:将java
源文件变成字节码文件的过程。例如javac
。JIT
编译器:将字节码文件变成机器码的过程。AOT
编译器:将java源文件变成机器码的过程(发展趋势)。
2.是否需要JIT
编译器将字节码翻译成机器指令,是根据代码的执行频率确定的,那些需要被翻译成机器指令的字节码称为“热点代码”。JIT
编译器会将“热点代码”直接编译成机器指令。
3.一个被多次调用的方法或者是一个循环次数较多的循环体都可以被称为“热点代码”。热点代码会通过JIT
编译器翻译成机器指令,由于这种编译发生在方法执行过程中,所有又称为“栈上替换”(因为方法的调用是发生在栈上的)。
4.一个方法要被调用多少次或者一个循环体要被执行多少次才会成为“热点代码”,是通过热点探测功能决定的。
5.目前HotSpot
采用的热点探测方式是基于计数器的热点探测。HotSpot
会为每一个方法建立2
个不同类型的计数器,分别为方法调用计数器和回边计数器。
方法调用计数器:统计方法的调用次数。
回边计数器:统计循环体执行的循环次数。
6.计数器默认的阈值在Client
模式下是1500
次,在Server
模式下是10000
次,超过这个阈值就会触发JIT
编译。可以通过-XX:CompileThreshold
来设置。
7.当一个方法被调用的时候,会先检查改方法是否存在被JIT
编译过的版本,如果存在,则优先调用编译后的本地代码执行。如果不存在,则此方法的调用计数器加1
,然后判断方法调用计数器和回边计数器之和是否超过计数器的阈值,如果超过,则向即时编译器提交一个该方法的代码编译请求,JIT
会将编译之和的机器码缓存起来(缓存到方法区),然后执行;如果不超过,则采用解释执行的方式。
8.如果不做任何的设置,方法调用计数器统计的并不是方法被调用的绝对次数,而是一个相对的执行频率,即一段时间之内方法被调用的次数
。当超过一定的时间限度,如果方法调用次数不足以让它被JIT
编译器进行编译,那么这个方法的调用计数器就会减少一半
,这个过程称为调用计数器的热度衰减,而这段时间称为半衰周期。
9.可以使用-XX:UseCounterDecay
关闭热度衰减,让方法调用计数器使用绝对计数,这样,只要运行时间足够长,绝大部分方法都会被编译成本地代码。
10.可以使用-XX:CounterHalfLifeTime
参数设置半衰周期,单位是秒。