JIT编译器是Java虚拟机(以下简称JVM)中效率高并且重要的组成部分之一。但是很多的程序并没有充分利用JIT的高性能优化能力,很多开发者甚至也并不清楚他们的程序有效利用JIT的程度。
  在本文中,我们将介绍一些简单的方法来验证你的程序是否对JIT友好。这里我们并不打算覆盖诸如JIT编译器工作原理这些细节。只是提供一些简单基础的检测和方法来帮助你的代码对JIT友好,进而得到优化。
  JIT编译的关键一点是JVM会自动地监控正在被解释器执行的方法。一旦某个方法被视为频繁调用,这个方法会被标记,进而编译成本地机器指令。这些频繁执行的方法的编译由后台的一个JVM线程来完成。在编译完成之前,JVM会执行这个方法的解释执行版本。一旦该方法编译完成,JVM会使用将方法调度表中该方法的解释的版本替换成编译后的版本。
  Hotspot虚拟机有很多JIT编译优化的技术,但是其中重要的一个优化技术是内联。在内联的过程中,JIT编译器有效地将一个方法的方法体提取到其调用者中,从而减少虚方法调用。举个例子,看如下的代码:
  public int add(int x, int y) {
  return x + y;
  }
  int result = add(a, b);
  当内联发生之后,上述代码会变成
  int result = a + b;
  上面的变量a和b替换了方法的参数,并且add方法的方法体已经复制到了调用者的区域。使用内联可以为程序带来很多好处,比如
  不会引起额外的性能损失
  减少指针的间接引用
  不需要对内联方法进行虚方法查找
  另外,通过将方法的实现复制到调用者中,JIT编译器处理的代码增多,使得后续的优化和更多的内联成为可能。
  内联取决于方法的大小。缺省情况下,含有35个字节码或更少的方法可以进行内联操作。对于被频繁调用的方法,临界值可以达到325个字节。我们可以通过设置-XX:MaxInlineSize=# 选项来修改大的临界值,通过设置?XX:FreqInlineSize=#选项来修改频繁调用的方法的临界值。但是在没有正确的分析的情况下,我们不应该修改这些配置。因为盲目地修改可能会对程序的性能带来不可预料的影响。
  由于内联会对代码的性能有大幅提升,因此让尽可能多的方法达到内联条件尤为重要。这里我们介绍一款叫做Jarscan的工具来帮助我们检测程序中有多少方法是对内联友好的。
  Jarscan工具是分析JIT编译的JITWatch开源工具套件中的一部分。和在运行时分析JIT日志的主工具不同,Jarscan是一款静态分析jar文件的工具。该工具的输出结果格式为CSV,结果中包含了超过频繁调用方法临界值的方法等信息。JITWatch和Jarscan是AdoptOpenJDK工程的一部分,该工程由Chris Newland领导。
  在使用Jarscan并得到分析结果之前,需要从AdoptOpenJDK Jenkins网站下载二进制工具(Java 7 工具,Java 8 工具)。
  运行很简单,如下所示
  ./jarScan.sh <jars to analyse>
  更多关于Jarscan的细节可以访问AdoptOpenJDK wiki进行了解。
  上面产生的报告对于开发团队的开发工作很有帮助,根据报告结果,他们可以查找程序中是否包含了过大而不能JIT编译的关键路径方法。上面的操作依赖于手动执行。但是为了以后的自动化,可以开启Java的-XX:+PrintCompilation 选项。开启这个选项会生成如下的日志信息:
  37    1      java.lang.String::hashCode (67 bytes)
  124   2  s!  java.lang.ClassLoader::loadClass  (58 bytes)
  其中,第一列表示从进程启动到JIT编译发生经过的时间,单位为毫秒。第二列表示的是编译id,表明该方法正在被编译(在Hotspot中一个方法可以多次去优化和再优化)。第三列表示的是附加的一些标志信息,比如s代表synchronized,!代表有异常处理。后两列分别代表正在编译的方法名称和该方法的字节大小。