一:解释器与编译器(概叙)
解释器与编译器各有优势:
1:当程序需要迅速启动运行的时候,解释器可以先发挥作用,省去编译的时间,立即执行。
2:随着时间推移,编译器逐渐发挥作用,更多的代码被编译成本地代码,以获得更高的运行效率。
3:当程序运行环境中内存资源限制较大时,可以使用解释器执行来解约内存,反制可以使用编译器执行来提高效率。
4:解释器还可以作为编译器激进优化(出现陷阱)时的一个“逃生门”(通过逆优化退回到解释状态继续执行)
程序使用哪个编译器,取决于虚拟机运行的模式,HotSpot虚拟机会根据自身版本与宿主机器的硬件性能自动选择运行模式。用户也可以通过参数:“-client”或"-server"来强制虚拟机是运行模式。
“混合模式”:解释器与编译器搭配使用的方式。
“解释模式”:用户使用参数“-Xint”强制使虚拟机不使用编译器,所有代码都使用解释方式执行。
“编译模式”:用户使用参数“-Xcomp”,此时将优先采用编译方式执行程序,但是解释器仍然要在编译无法进行的情况下介入执行过程。
二:编译对象与触发条件
热点代码:当虚拟机发现某个方法或代码块的运行特别频繁的时候,就会把这些代码认定为“热点代码”,为了提高其执行效率,在运行时,虚拟机会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化。完成这个任务的编译器叫做即使编译器(Just In Time Compiler —— JIT编译器)。
热点代码分类:
1:被多次调用的方法
2:被多次执行的循环体
对于第二种,虽然触发编译的是循环体,但编译器仍然会编译整个方法,这样因为编译发生在方法的执行过程中,因此被称为“栈上替换”(方法栈帧还在栈上,方法就被替换了)。
热点深测:判断一段代码是不是热点代码,是否需要出发即时编译的行为。
热点深测判定主要的两种方式:
1:基于采样点的热点深测
使用这种方法的虚拟机,会周期性的检查各个线程的栈顶,当发现某些方法经常出现在栈顶时,就将这些方法记作“热点方法”。(实现简单、高效,但难以精准的确定一个方法的热度,容易受线程阻塞或其他外界因素的影响)
2:基于计数器的热点深测
虚拟机会再每个方法上建立计数器,统计方法的执行次数,当次数超过一定的阈值,就认定他为“热点方法”。(实现麻烦、要维护计数器,但更准确更严谨)
HotSpot虚拟机使用的第二种方法——基于计数器的热点深测,他为每个方法准备了两个计数器:
1:方法调用计数器 :统计方法的执行次数。
2:回边计数器:统计一个方法体中循环体代码执行的次数。
阈值设定:
Client模式下:1500次 | Server模式下:10000次
该阈值可以通过参数 -XX : CompileThreshold 来设定。
方法调用过程:
方法调用计数器热度的衰减:如果不做任何设置,方法调用计数器统计的并不是方法被调用的绝对次数,而是一个相对的执行频率:即一段时间内被调用的次数,当超过一定时间限度,方法调用次数仍然不足时,这个方法的调用计数器会被减少一半。这段时间就称之为:方法统计的半衰周期
进行热度衰减的动作是在虚拟机进行垃圾回收时顺便进行的。
关闭衰减:可以使用参数-XX: -UseCounterDecay来设置
半衰周期: -XX : CounterhalfLifeTime (单位:秒)
回边的概念:在字节码中遇到控制流向后跳转的指令。
下图、回边计数器触发即时编译过程:
回边计数器没有热度衰减的过程,因此回边计数器统计的就是该方法循环体执行的绝对次数。当回边计数器溢出的时候,它会把方法计数器的值也调整到溢出状态,这样下次再进入该方法的时候,就会执行标准编译过程。