运行期的优化——热点代码

一:解释器与编译器(概叙)

解释器与编译器各有优势:

    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 (单位:秒)

    回边的概念:在字节码中遇到控制流向后跳转的指令。

    下图、回边计数器触发即时编译过程:

运行期的优化——热点代码

回边计数器没有热度衰减的过程,因此回边计数器统计的就是该方法循环体执行的绝对次数。当回边计数器溢出的时候,它会把方法计数器的值也调整到溢出状态,这样下次再进入该方法的时候,就会执行标准编译过程。



你可能感兴趣的:(java,计数器,热点代码)