JVM:HotSpot的即时编译器
Posted 漫步君
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM:HotSpot的即时编译器相关的知识,希望对你有一定的参考价值。
当虚拟机发现某个方法或者代码块的运行特别频繁时,就会把这些代码认定为热点代码(Hot Spot Code)。为了提高热点代码的执行效率,在运行时虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器成为即时编译器(JIT编译器)。
即时编译器并不是虚拟机必须的部分,但是它的性能好坏、代码优化程度的高低却是衡量一款商用虚拟机优秀与否的最关键的指标之一,它也是虚拟机中最核心也是最能体现虚拟机技术水平的部分。
我们以HotSpot为例,它采用解释器与编译器并存的架构并有以下优点:
当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行;
随着时间的推移,编译器发挥作用。他把越来越多的代码编译成本地代码之后,可以获取更高的执行效率;
当程序运行环境中内存资源限制较大,可使用解释执行节约内存,反之可以使用编译执行提升效率。
在HotSpot虚拟机中内置了两个即时编译器Client Compiler和Server Compiler,又被称为C1编译器和C2编译器。目前主流的HotSpot虚拟机(Sun系列jdk1.7及之前版本的虚拟机中),默认采用解释器与其中一个编译器直接配合的方式工作,程序使用哪个编译器,取决于虚拟机运行的模式。HotSpot虚拟机会根据自身版本与宿主机器的硬件性能自动选择运行模式。
为了在程序启动相应速度和运行效率之间达到最佳平衡,HotSpot虚拟机还会逐渐启用分层编译的策略。分层编译根据编译器、优化的规模与耗时,也划分出不同的编译层次:
>第0层:程序解释执行,解释器不开启性能监控功能,可触发第1层编译;
>第1层:也称为C1编译,将字节码编译为本地代码,进行简单、可靠的优化,如果有不要将加入性能监控的逻辑。
>第2层(或者2层以上):也称为C2编译,也是将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。
试试分层编译后,C1和C2将会同时运作,许多代码都可能会被多次编译。用C1可以获取更高的编译速度,用C2可以获取更好的编译质量,在解释执行的时候也无需在承担手机性能监控信息的任务。
我们上文提到的在运行过程中会被即时编译器编译的热点代码有两类:被多次调用的方法以及被多次执行的循环体。
判断一段代码是不是热点代码、是不是需要触发即时编译,这样的行为又称为热点探测。目前热点探测的方法有两种:基于采样的热点代码和基于计数器的热点探测。
基于采样的热点代码:周期性检查栈顶,查看某方法出现的次数。这个方法简单高效、容易获取方法调用联系,但是很难精确确认一个方法的热度。
基于计数器的热点探测:为每个方法建立计数器来统计方法的执行次数。这个方法实现麻烦、不容易获取调用关系,但是统计结果精确严谨,目前HotSpot采用的就是这种方式。
HotSpot为每个方法准备了两类计数器:方法调用计数器和回边计数器
这个计数器用于统计方法被用于的次数。当一个方法被调用时,会先检查该方法是否存在被JIT编译过的版本,如果存在,则有显示使用编译后的本地版本来执行。如果不存在已被编译过的版本,则将此方法调用计数器值加1,然后判断方法调用计数器与回边计数器值之和是否超过方法调用计数器的阈值。如果已超过阈值,那么将会向即时编译器提交一个该方法的代码编译请求。
如果不做任何设置,方法调用计数器统计的并不是方法被调用的绝对次数,二是一个相对的执行频率,即一段时间内方法被调用的次数。当超过一定时间限度时,方法调用计数器就会进行热度衰减,即将调用计数器减少一半。
与方法计数器不同,回边计数器没有技术热度衰减的过程,因此这个计数器统计的就是该方法循环执行的绝对次数。当计数器溢出的时候,他还会把方法计数器的值也调整到溢出状态,这样下次再进入该方法的时候就会执行标准编译过程。
随笔,是记忆的一种延伸
以上是关于JVM:HotSpot的即时编译器的主要内容,如果未能解决你的问题,请参考以下文章