1.1 延迟编译delayed compilation

一 为什么要延迟编译

  在经济学里,有二八法则,说的是80%的财富集中在20%的人手里,然后这20%的人又服从二八法则,那另外80%的人也符合二八法则。
  研究发现,在JVM的运行过程中,80%的处理器时间执行20%的代码,这个过程也可以递归。这20%的代码被称为热点hotspot,而被执行的方法同样被称为热点方法。所以只有将这20%的代码编译为本地代码才能达到最优的性能。因为大部分代码执行次数很少甚至只执行一次,所以没必要编译。因为解释一次,肯定比编译再执行一次更节省资源,也更快。只编译执行次数多的代码是JIT(Just-In-Time)的核心要义,因为JIT的意思就是及时。
  从JAVA8开始,Oracle对Sun Hotspot虚拟机进行优化,开始集成JRocket VM中的JIT编译器。
  大部分人都知道JIT分为Client Compiler和Server Compier。这两种都有不同的实现,简要下如下表:

编译器实现
Client CompilerC1 Compiler
Server CompierC2 Compiler
Graal Compiler
Shark Compiler(已经废弃)

二 如何检测热点代码

  知道了只编译热点代码后,马上要想的是如何实现。JVM使用了两个计数器来实现。一个是方法调用计数器invocation counter,一个是回边计数器backedge_counter。我为什么敢这么说呢?这年头不翻源码都不好意思写文章了,哈哈,必须讲证据,请看源码hotspot/src/share/vm/oops/methodCounters.hpp

class MethodCounters: public MetaspaceObj {
 friend class VMStructs;
 private:
  int               _interpreter_invocation_count; // Count of times invoked (reused as prev_event_count in tiered)
  u2                _interpreter_throwout_count; // Count of times method was exited via exception while interpreting
  u2                _number_of_breakpoints;      // fullspeed debugging support
  InvocationCounter _invocation_counter;         // Incremented before each activation of the method - used to trigger frequency-based optimizations
  InvocationCounter _backedge_counter;           // Incremented before each backedge taken - used to trigger frequencey-based optimizations

  这里我再讲下回边,回边是图论/图算法中的概念,回边是图中DFS搜索中发现的指向前驱节点的边,也就是形成环的那条边。把程序的执行看成图,回边就代表了回到循环起点的最后一个步骤。可以这么说回边计数器就是循环计数器。回边的检测是用宏定义写的一个宏函数,源码hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp:

#define DO_BACKEDGE_CHECKS(skip, branch_pc)                                                         \
    if ((skip) <= 0) {                                                                              \
      MethodCounters* mcs;                                                                          \
      GET_METHOD_COUNTERS(mcs);                                                                     \
      if (UseLoopCounter) {                                                                         \
        bool do_OSR = UseOnStackReplacement;                                                        \
        mcs->backedge_counter()->increment();                                                       \
        if (do_OSR) do_OSR = mcs->backedge_counter()->reached_InvocationLimit();                    \
        if (do_OSR) {                                                                               \
          nmethod*  osr_nmethod;                                                                    \
          OSR_REQUEST(osr_nmethod, branch_pc);                                                      \
          if (osr_nmethod != NULL && osr_nmethod->osr_entry_bci() != InvalidOSREntryBci) {          \
            intptr_t* buf = SharedRuntime::OSR_migration_begin(THREAD);                             \
            istate->set_msg(do_osr);                                                                \
            istate->set_osr_buf((address)buf);                                                      \
            istate->set_osr_entry(osr_nmethod->osr_entry());                                        \
            return;                                                                                 \
          }                                                                                         \
        }                                                                                           \
      }  /* UseCompiler ... */                                                                      \
      mcs->invocation_counter()->increment();                                                       \
      SAFEPOINT;                                                                                    \
    }

  可以从上面的代码里看到,里面还调用了方法调用计数器。然后在比较运算符和goto\gotow指令的实现中,使回边计算器递增。比如实现==与!=的宏函数:

#define COMPARISON_OP2(name, comparison)                                     \
      COMPARISON_OP(name, comparison)                                        \
      CASE(_if_acmp##name): {                                                \
          int skip = (STACK_OBJECT(-2) comparison STACK_OBJECT(-1))          \
                       ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3;            \
          address branch_pc = pc;                                            \
          UPDATE_PC_AND_TOS(skip, -2);                                       \
          DO_BACKEDGE_CHECKS(skip, branch_pc);                               \
          CONTINUE;                                                          \
      }

  方法调用计数器除了上面说的回边计数器里调用了以外,还在另一个地方调用了,就是方法入口method entry。源码还是源码hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp:

case method_entry: {
      THREAD->set_do_not_unlock();
      // count invocations
      assert(initialized, "Interpreter not initialized");
      if (_compiling) {
        MethodCounters* mcs;
        GET_METHOD_COUNTERS(mcs);
        if (ProfileInterpreter) {
          METHOD->increment_interpreter_invocation_count(THREAD);
        }
        mcs->invocation_counter()->increment();
        if (mcs->invocation_counter()->reached_InvocationLimit()) {
            CALL_VM((void)InterpreterRuntime::frequency_counter_overflow(THREAD, NULL), handle_exception);

            // We no longer retry on a counter overflow

            // istate->set_msg(retry_method);
            // THREAD->clr_do_not_unlock();
            // return;
        }
        SAFEPOINT;
      }

      if ((istate->_stack_base - istate->_stack_limit) != istate->method()->max_stack() + 1) {
        // initialize
        os::breakpoint();
      }

三 怎么衡量热点代码

编译器调用次数回边次数
C11500100000
C210000100000

  我是讲究证据的,哈哈,死命找源码。
  在JVM源码中hotspot/src/cpu/x86/vm/c1_globals_x86.hpp有这么一段:

define_pd_global(intx, CompileThreshold,             1500 );
define_pd_global(intx, BackEdgeThreshold,            100000);

  在JVM源码中hotspot/src/cpu/x86/vm/c2_globals_x86.hpp有这么一段:

define_pd_global(intx, CompileThreshold,             10000);
define_pd_global(intx, BackEdgeThreshold,            100000);
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

醒过来摸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值