十、JVM(HotSpot)晚期(运行时)优化

注:本博文主要是基于JDK1.7会适当加入1.8内容。

Java程序最初是通过解释器进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这段代码认定为“热点代码”,为了提高这部分代码的执行效率,在运行时,虚拟机会将这些代码编译成与本地平台相关的机器码,并进行各个层次的优化,完成这个任务的编译器成为即时编译器(JIT)

1、HotSpot虚拟机内的即时编译器

1)解释器与编译器
当程序需要迅速启动和执行的时候,解释器优先发挥作用,省去编译的时间,立即执行。在程序运行后,随着时间的推移,把越来越多的代码编译成本地代码以后,获取更高的执行效率。当程序运行环境中的内存资源限制较大,可以使用解释执行节约内存,反之,可以使用编译器执行提升效率。同时,解释器可以作为编译器进行优化时的逃生门,让编译器根据概率选择一些大多数都能提升运行速度的优化手段,当激进优化的假设不成立时,退回解释状态继续执行。

HotSpot默认为mixed mode,可以通过-Xint改为解释器模式interpreted mode,可以通过-Xcomp改为编译器模式(还会有解释执行,激进优化不成立时)compiled mode

HotSpot内置两个即时编译器:Client CompilerServer Compiler,参数-client-server,JDK1.6后,分层编译:

  • 第0层,程序解释执行,解释器不开启性能监控功能,可触发第一层编译;
  • 第1层,也称为C1编译,将字节码编译成本地代码,进行简单、可靠的优化,如有必要将加入性能监控的逻辑。
  • 第2层,也成为C2编译,也是将字节码编译成本地代码,但是用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。

实施分层编译后,Client Compiler和Server Compiler将会同时工作,许多代码可能被多次编译,用Client Compiler获取更高的编译速度,用Server Compiler获取更好的编译质量,在解释执行时候无须承担收集性能监控信息的任务。

2、编译对象和触发条件

前面说,运行过程中被即时编译器编译的“热点代码”有两类:被多次调用的方法;被多次执行的代码。
如何判断“多次”:

  • 基于采用的热点探嗅:虚拟机会周期性的检查各个线程的栈顶,如果发现某个(某些)方法经查出现在栈顶,那这个(或这些)方法就是热点方法。好处:简单,高效,可以容易获取方法的调用关系;缺点:很难精确地确认一个方法的热度,容易出现因为线程阻塞或别的外界因素而扰乱热点探测。
  • 基于计数器的热点探测(HotSpot采用):虚拟机为每个方法,甚至是方法块建立计数器,统计方法执行次数,如果方法执行次数超过一定阈值就认为它是热点方法。优点:统计结果精确、严谨;缺点:麻烦,需要为每个方法建立计数器并维护,而且不能直接获取方法调用关系。

基于计数器的热点探测,每个方法都有两类计数器:方法调用计数器和回边计数器。
1)方法调用计数器(-XX:CompileThreshole默认值Client模式1500,Server模式10000),下图为Client模式下,Server模式较为复杂。
方法调用计数器

-XX:-UseConterDecay关闭热度,-XX:CounterHalfLifeTime设置半衰周期时间。如果不做任何设置,方法计数器统计的并非绝对次数,而是一个相对的执行频率,即一段时间之内方法被调用的次数。当超过一定的时间限度,如果方法的调用次数仍然不足以让它提交给即时编译器编译,那这个方法调用次数将会减半,这个过程称为方法调用计数器热度的衰减。

2)回边计数器:统计方法中循环体代码执行次数,在字节码中遇到控制流后跳转的指令称为“回边”,-XX:BackEdgeThreshole回边次数,但是当前虚拟机实际未使用该参数,使用-XX:OnStackReplacePercentage间接调整回边计数器阈值,下图为Client模式,Server模式更加复杂。
回边计数器

  • Client模式下:方法调用计数器阈值(CompileThreshold)*OSR比率(OnStackReplacePercentage)/100。默认值CompileThreshold为1500,OnStackReplacePercentage为933,最终回边计数器阈值为13995.
  • Server模式下:方法调用计数器阈值(CompileThreshold)*(OSR比率(OnStackReplacePercentage) -解释器监控比率(InterpreterProfilePercentage))/100。默认值CompileThreshold为10000,OnStackReplacePercentage为140,InterpreterProfilePercentage为33,最终回边计数器阈值为10700.

回边计数器没有衰减过程,计数器统计的是该方法执行的绝对次数,当计数溢出时,它会把方法计数器的值调整到溢出状态,下次再进入方法时会执行标准编译过程。

3、编译过程

无论是方法调用技术器还是回边计数器触发的编译执行,在代码编译未完成都会以解释方式执行。通过-XX:BackgroundCOmpilation禁止后台编译,一旦达到JIT编译条件,执行线程向虚拟机提交编译请求后将一直等待,直到编译过程完成后,再开始执行编译器输出的本地代码。

编译优化技术

  • 语言无关的经典技术之一:公共子表达式消除
  • 语言有关的经典技术之一:数组范围检查消除
  • 最重要的优化技术之一:方法内联
  • 最前沿的优化技术之一:逃逸分析,基本行为就是分析对象动态作用域,当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,称为方法逃逸;可能被外部线程访问到,譬如赋值给类变量或其他线程中访问的实例变量,称为线程逃逸。

猜你喜欢

转载自blog.csdn.net/zhangwei408089826/article/details/81780217