热点代码探测确定何时JIT

方法调用计数器:

  • 这个计数器就用统计方法被调用的次数,它的默认阈值在client模式下是1500次,在Server模式下是10000次。超过这个阈值,就会触发JIT编译。
  • 这个阈值可以通过虚拟机参数:-XX:compileThreshold来人为设定。
  • 当一个方法被调用时,会先检查该方法是否存在被JIT编译过的版本,如果存在,则优先使用编译后的本地代码来执行。如果不存在已被编译过的版本,则将此方法的调用计数器值加1,然后判断方法调用计数器与回边计数器值之和是否超过方法调用计数器的阈值。如果已超过阈值,那么将会向即时编译器提交一个该方法的代码编译请求。

热度衰减

  • 如果不做任何设置,方法调用计数器统计的并不是方法被调用的次数,而是一个相对的执行频率,即一段时间内方法被调用的次数。当超过一定的时间限度,如果方法的调用次数仍然不足以让它交给即时编译器编译,那么这个方法的调用计数器就会减少一半,这个过程称为方法调用计数器热度的衰减(Counter Decay),而这段时间就称为此方法统计的半衰周期(Counter Half Life Time)。
  • 进行热度衰减的工作是在虚拟机进行垃圾回收时顺便进行的,可以使用虚拟机参数:-XX:-UseCounterDecay来关闭热度衰减,让方法计数器统计方法调用的绝对次数,这样,只要系统运行时间足够长,绝大部分方法都会编译成本地代码。
  • 另外,可以使用:-XX:CounterHalfLifeTime参数设置半衰周期的时间,单位是秒。

HotSpot VM执行方式

缺省情况下HotSpot VM是采用解释器与即时编译器并存的架构,当然开发人员可以根据具体的应用场景,通过命令显式地为Java虚拟机知道在运行时到底是完全采用解释器执行,还是完全采用即时编译器执行。

  • -Xint:完全采用解释器模式执行
  • -Xcomp:完全采用即时编译器执行程序,如果即时编译出现问题,解释器会接入执行。
  • -Xmixed:采用解释器+即时编译器的混合模式共同执行程序
  • java -Xint -version

HotSpot VM中JIT分类

在HotSpot VM中内嵌有两个JIT编译器,分别为Client Compiler和Server Compiler,但大多数情况下我们简称为C1编译器和C2编译器。

  • -client:指定Java虚拟机运行在Client模式下,并使用C1编译器;
    • C1编译器会对字节码进行简单和可靠的优化,耗时短。以达到更快的编译速度。
  • -server:指定Java虚拟机运行在Server模式下,并使用C2编译器
    • C2进行耗时较长的优化,以及激进优化。但优化的代码执行效率更高

C1和C2编译器不同的优化策略

  • 在不同的编译器上有不同的优化策略,C1编译器上主要有方法内联,去虚拟化
    • 方法内联:将引用的函数代码编译到引用点处,这样可以减少栈帧的生成,减少参数传递以及跳转过程
    • 去虚拟化:对唯一的实现类进行内联
  • C2的优化主要是在全局层面,逃逸分析是优化的基础。基于逃逸分析在C2上有如下几种优化:
    • 标量替换:标量值代替对象的属性值
    • 栈上分配:对于未逃逸的对象分配对象在栈而不是堆
    • 同步消除:清除同步操作,通常指synchronized