晚期(运行期)优化

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

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

    解释器与编译器两者各有优势: 当程序需要迅速启动和执行的时候, 解释器可以首先发挥作用, 省去编译的时间, 立即执行。 在程序运行后, 随着时间的推移, 编译器逐渐发挥作用, 把越来越多的代码编译成本地代码之后, 可以获取更高的执行效率。

     1.1 C1编译器和C2编译器

     HotSpot虚拟机中内置了两个即时编译器, 分别称为Client Compiler和Server Compiler, 或者简称为C1编译器和C2编译器(也叫Opto编译器) 。 (新版64位jdk已经取消Client模式,只有Server模式)。

     解释器与编译器搭配使用的方式在虚拟机中称为“混合模式” (Mixed Mode) , 用户可以使用参数“-Xint” 强制虚拟机运行于“解释模式” (Interpreted Mode) , 这时编译器完全不介入工作, 全部代码都使用解释方式执行。 另外, 也可以使用参数“-Xcomp” 强制虚拟机运行于“编译模式” (Compiled Mode),这时将优先采用编译方式执行程序, 但是解释器仍然要在编译无法进行的情况下介入执行过程, 可以通过虚拟机的“-version” 命令的输出结果显示出这3种模式.

      HotSpot虚拟机还会逐渐启用分层编译(Tiered Compilation)的策略,分层编译根据编译器编译、 优化的规模与耗时, 划分出不同的编译层次,其中包括:

      第0层, 程序解释执行, 解释器不开启性能监控功能(Profiling) , 可触发第1层编译。

      第1层, 也称为C1编译, 将字节码编译为本地代码, 进行简单、 可靠的优化, 如有必要将加入性能监控的逻辑。

      第2层(或2层以上) , 也称为C2编译, 也是将字节码编译为本地代码, 但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。

 1.2编译对象与触发条件

  在运行过程中会被即时编译器编译的“热点代码” 有两类, 即:

   被多次调用的方法。

   被多次执行的循环体。

   第一种情况, 编译器会以整个方法作为编译对象, 这种编译也是虚拟机中标准的JIT编译方式。

   第二种情况, 编译器依然会以整个方法(而不是单独的循环体) 作为编译对象。 因为编译发生在方法执行过程之中, 因此称之为栈上替换(On Stack Replacement, 简称为OSR编译, 即方法栈帧还在栈上, 方法就被替换了)  

   A.热点探测

   判断一段代码是不是热点代码, 是不是需要触发即时编译, 这样的行为称为热点探测(Hot Spot Detection) , 目前主要的热点探测判定方式有两种:

   1).基于采样的热点探测(Sample Based Hot Spot Detection)

   采用这种方法的虚拟机会周期性地检查各个线程的栈顶, 如果发现某个(或某些) 方法经常出现在栈顶, 那这个方法就是“热点方法” 。

  2).基于计数器的热点探测(Counter Based Hot Spot Detection)

  采用这种方法的虚拟机会为每个方法(甚至是代码块) 建立计数器, 统计方法的执行次数, 如果执行次数超过一定的阈值就认为它是“热点方法” 。

 B.HotSpot中的热点探测

   在HotSpot虚拟机中使用的是基于计数器的热点探测方法, 它为每个方法准备了两类计数器:方法调用计数器(Invocation Counter) 和回边计数器(Back Edge Counter):

   1).方法调用计数器

这个计数器就用于统计方法被调用的次数, 它的默认阈值在Client模式下是1500次, 在Server模式下是10 000次, 这个阈值可以通过虚拟机参数-XX:CompileThreshold来设定。

如果不做任何设置, 方法调用计数器统计的并不是方法被调用的绝对次数, 而是一个相对的执行频率, 即一段时间之内方法被调用的次数。 当超过一定的时间限度, 如果方法的调用次数仍然不足以让它提交给即时编译器编译, 那这个方法的调用计数器就会被减少一半, 这个过程称为方法调用计数器热度的衰减(Counter Decay) , 而这段时间就称为此方法统计的半衰周期(Counter Half Life Time) 。

  2).回边计数器

    回边计数器, 它的作用是统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边” (Back Edge) 。

   虚拟机运行在Server模式下, 回边计数器阈值的计算公式为:方法调用计数器阈值(CompileThreshold) ×(OSR比率(OnStackReplacePercentage) -解释器监控比率(InterpreterProfilePercentage) /100,其中OnStackReplacePercentage默认值为140,InterpreterProfilePercentage默认值为33, 如果都取默认值, 那Server模式虚拟机回边计数器的阈值为10700。

1.3 编译过程

      Server Compiler则是专门面向服务端的典型应用并为服务端的性能配置特别调整过的编译器, 也是一个充分优化过的高级编译器, 它会执行所有经典的优化动作, 如无用代码消除(Dead Code Elimination) 、 循环展开(Loop Unrolling) 、 循环表达式外提(Loop Expression Hoisting) 、 消除公共子表达式(Common Subexpression Elimination) 、 常量传播(Constant Propagation) 、基本块重排序(Basic Block Reordering) 等, 还会实施一些与Java语言特性密切相关的优化技术, 如范围检查消除(Range Check Elimination) 、 空值检查消除(Null Check Elimination)等。 另外, 还可能根据解释器或Client Compiler提供的性能监控信息, 进行一些不稳定的激进优化, 如守护内联(Guarded Inlining) 、 分支频率预测(Branch Frequency Prediction) 等。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值