Java的即时编译

分层编译模式

HotSpot虚拟机包含多个即时编译器C1、C2

C1

  1. 编译效率快,对应参数 -client,针对执行时间较短、或者对启动性能有要求的程序
  2. 目的是使程序尽快进入编译执行阶段,所以没有收集很多,所以编译速度很快

C2

  1. 生成代码执行效率快,对应参数 -server,针对执行时间长的、对峰值性能有要求的程序。
  2. 将编译成机器代码的时候需要收集大量的统计信息以便在编译的时候优化,所以编译出来的代码执行效率快

Java 7引入了分层编译(对应参数 -XX:+TieredCompilation),综合了C1的启动性能优势和C2的峰值性能优势

分层编译将Java虚拟机的执行状态分为了五个层次:

  1. 解析执行

  2. 执行不带profiling的C1代码

  3. 执行仅带方法调用次数以及循环回边执行次数profilling的C1代码

  4. 执行带所有profiling的C1代码

  5. 执行C2代码

profiling说指程序执行过程中,收集能够反映程序执行状态的数据。profile是收集运行时状态信息,用于编译器优化,当然,收集信息也是耗性能的,所以,也是有前提条件的,当存在优化的可能性时才去费劲收集相关信息

1.8是默认开启分层编译,如果关闭Java虚拟机直接选择C2

即时编译的触发

Java虚拟机是根据方法的调用次数以及循环回边(往回跳转的指令)的执行次数来触发即时编译,Java虚拟机在0、2、3层执行状态时进行profiling,其中就包含方法的调用次数和循环回边的执行次数。

public static void foo(Object obj) {
  int sum = 0;
  for (int i = 0; i < 200; i++) {
    sum += i;
  }
}

上面这段代码将被编译为下面的字节码。其中,偏移量为 18 的字节码将往回跳至偏移量为 7 的字节码中。在解释执行时,每当运行一次该指令,Java虚拟机便会将方法的循环回边计数器加1。

public static void foo(java.lang.Object);
  Code:
     0: iconst_0
     1: istore_1
     2: iconst_0
     3: istore_2
     4: goto 14
     7: iload_1
     8: iload_2
     9: iadd
    10: istore_1
    11: iinc 2, 1
    14: iload_2
    15: sipush 200
    18: if_icmplt 7
    21: return

在即时编译过程,我们会识别循环的头部和尾部,在上面这段代码,循环的头部是偏移量为14的字节码,尾部为偏移量为11的字节码,它们的控制流边就是循环回边,也就是C1将在这个位置插入增加循环回边计数器的代码。

在不启动分层编译的时候,当方法的调用次数和循环回边的次数的和,超过由参数XX:CompileThreshold指定的阈值时(C1:值是 1500,C2:值是10000),便会触发即时编译。

当启动分层编译时,阈值的大小是动态调整的。

在比较阈值时,Java 虚拟机会将阈值与某个系数 s 相乘。该系数与当前待编译的方法数目成正相关,与编译线程的数目成负相关。

OSR编译

决定一个方法是否为热点代码的因素有两个:

  1. 方法的调用次数
  2. 循环回边的执行次数

即时编译就是根据这两个计数器的和来触发的

除了以方法为单位的即时编译之外,Java 虚拟机还存在着另一种以循环为单位的即时编译,叫做 On-Stack-Replacement(OSR)编译。循环回边计数器便是用来触发这种类型的编译的。

OSR 实际是一种技术,在程序执行过程中,动态地替换Java方法栈桢,使程序能够在非方法入口处进行解释执行和编译后的代码之间的切换,去优化(从执行机器码切换回解释执行)采用的技术也可以称为OSR,OSR 编译可以用来解决单次调用方法包含热循环的性能优化问题。

OSR 编译在正常的应用程序中并不多见。它只在基准测试时比较常见。

总结

  1. 从 Java 8 开始,Java 虚拟机默认采用分层编译的方式。它将执行分为五个层次,分为为 0 层解释执行,1 层执行没有 profiling 的 C1 代码,2 层执行部分 profiling 的 C1 代码,3 层执行全部 profiling 的 C1 代码,和 4 层执行 C2 代码。

  2. 通常情况下,方法会首先被解释执行,然后被 3 层的 C1 编译,最后被 4 层的 C2 编译。

  3. 即时编译是由方法调用计数器和循环回边计数器触发的。在使用分层编译的情况下,触发编译的阈值是根据当前待编译的方法数目动态调整的。

  4. OSR 是一种能够在非方法入口处进行解释执行和编译后代码之间切换的技术。OSR 编译可以用来解决单次调用方法包含热循环的性能优化问题

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值