Top


定義場所(file name)

hotspot/src/share/vm/compiler/compileBroker.cpp

説明(description)

// ------------------------------------------------------------------
// CompileBroker::compile_method
//
// Request compilation of a method.

名前(function name)

void CompileBroker::compile_method_base(methodHandle method,
                                        int osr_bci,
                                        int comp_level,
                                        methodHandle hot_method,
                                        int hot_count,
                                        const char* comment,
                                        TRAPS) {

本体部(body)

  {- -------------------------------------------
  (1) まだ CopmilerBroker クラスの初期化が終わってなければ 
      (JIT コンパイルは実行できないので) ここでリターン.

      (なお, _initialized は CompileBroker::compilation_init() で true にセットされる)
      ---------------------------------------- -}

      // do nothing if compiler thread(s) is not available
      if (!_initialized ) {
        return;
      }

  {- -------------------------------------------
  (1) (assert)
      ---------------------------------------- -}

      guarantee(!method->is_abstract(), "cannot compile abstract methods");
      assert(method->method_holder()->klass_part()->oop_is_instance(),
             "sanity check");
      assert(!instanceKlass::cast(method->method_holder())->is_not_initialized(),
             "method holder must be initialized");

  {- -------------------------------------------
  (1) (トレース出力)
      ---------------------------------------- -}

      if (CIPrintRequests) {
        tty->print("request: ");
        method->print_short_name(tty);
        if (osr_bci != InvocationEntryBci) {
          tty->print(" osr_bci: %d", osr_bci);
        }
        tty->print(" comment: %s count: %d", comment, hot_count);
        if (!hot_method.is_null()) {
          tty->print(" hot: ");
          if (hot_method() != method()) {
              hot_method->print_short_name(tty);
          } else {
            tty->print("yes");
          }
        }
        tty->cr();
      }

  {- -------------------------------------------
  (1) もし対象のメソッドの JIT コンパイルが既に終わっていた場合は, (何もすることがないので) ここでリターン
      ---------------------------------------- -}

      // A request has been made for compilation.  Before we do any
      // real work, check to see if the method has been compiled
      // in the meantime with a definitive result.
      if (compilation_is_complete(method, osr_bci, comp_level)) {
        return;
      }

  {- -------------------------------------------
  (1) (デバッグ用の処理) (#ifndef PRODUCT 時にのみ実行)
      ---------------------------------------- -}

    #ifndef PRODUCT
      if (osr_bci != -1 && !FLAG_IS_DEFAULT(OSROnlyBCI)) {
        if ((OSROnlyBCI > 0) ? (OSROnlyBCI != osr_bci) : (-OSROnlyBCI == osr_bci)) {
          // Positive OSROnlyBCI means only compile that bci.  Negative means don't compile that BCI.
          return;
        }
      }
    #endif

  {- -------------------------------------------
  (1) CompileBroker::compilation_is_in_queue() を呼んで,
      同じ JIT コンパイル要求が既に出されていないかどうかを確認する.
      出されていた場合は (同じ要求を 2回出す必要は無いので) ここでリターン.

      (なおコメントによると, 何度も JIT コンパイル要求が出てはリターンが繰り返される事態を防ぐため,
       このケースでは InvocationCounter を少し減らしてもいいかもしれない, とのこと.
       (まぁこれは設計ポリシーの問題なので "open compilation policy issue" とのこと).
       あるいは, ブロックするコンパイル要求の場合は,
       既に出されているコンパイル要求が完了するまでここで待たせてもいいが
       その場合は待ち受けるための仕組みが必要, とのこと)
      ---------------------------------------- -}

      // If this method is already in the compile queue, then
      // we do not block the current thread.
      if (compilation_is_in_queue(method, osr_bci)) {
        // We may want to decay our counter a bit here to prevent
        // multiple denied requests for compilation.  This is an
        // open compilation policy issue. Note: The other possibility,
        // in the case that this is a blocking compile request, is to have
        // all subsequent blocking requesters wait for completion of
        // ongoing compiles. Note that in this case we'll need a protocol
        // for freeing the associated compile tasks. [Or we could have
        // a single static monitor on which all these waiters sleep.]
        return;
      }

  {- -------------------------------------------
  (1) カレントスレッドが java.lang.ref の pending_list_lock をロックしていないかどうかチェックしておく.
      もしロックしている場合は, このまま処理するとデッドロック等の恐れも考えられるので
      JIT コンパイル処理は行わずに, ここでリターン
      ---------------------------------------- -}

      // If the requesting thread is holding the pending list lock
      // then we just return. We can't risk blocking while holding
      // the pending list lock or a 3-way deadlock may occur
      // between the reference handler thread, a GC (instigated
      // by a compiler thread), and compiled method registration.
      if (instanceRefKlass::owns_pending_list_lock(JavaThread::current())) {
        return;
      }

  {- -------------------------------------------
  (1) (変数宣言など)
      ---------------------------------------- -}

      // Outputs from the following MutexLocker block:
      CompileTask* task     = NULL;
      bool         blocking = false;
      CompileQueue* queue  = compile_queue(comp_level);

  {- -------------------------------------------
  (1) 以下の処理は compile_queue (CompileQueue オブジェクト) のロックを取得した状態で(排他的に)行う.
      ---------------------------------------- -}

      // Acquire our lock.
      {
        MutexLocker locker(queue->lock(), THREAD);

    {- -------------------------------------------
  (1.1) CompileBroker::compilation_is_in_queue() を呼んで,
        同じ JIT コンパイル要求が既に出されていないかどうかを確認する.
        出されていた場合は (同じ要求を 2回出す必要は無いので) ここでリターン.

        (上で一度チェックしているが, ロックを取った後でもう一度きちんとチェックし直している)
        ---------------------------------------- -}

        // Make sure the method has not slipped into the queues since
        // last we checked; note that those checks were "fast bail-outs".
        // Here we need to be more careful, see 14012000 below.
        if (compilation_is_in_queue(method, osr_bci)) {
          return;
        }

    {- -------------------------------------------
  (1.1) もし対象のメソッドの JIT コンパイルが既に終わっていた場合は,
        (何もすることがないので) ここでリターン

        (上で一度チェックしているが, ロックを取った後でもう一度きちんとチェックし直している)
        ---------------------------------------- -}

        // We need to check again to see if the compilation has
        // completed.  A previous compilation may have registered
        // some result.
        if (compilation_is_complete(method, osr_bci, comp_level)) {
          return;
        }

    {- -------------------------------------------
  (1.1) (この段階では, 今回の JIT コンパイル要求について
        同じ要求が出されていたり完了したりしておらず, また JIT コンパイルが禁止されてもいない,
        と分かっている)

        CompileBroker::assign_compile_id() を呼んで
        この JIT コンパイル要求用に新しい compile_id を取得する.

        (なお, デバッグ用のオプションである CIStart, CIStop, CIStartOSR, CIStopOSR が指定されている場合,
        CompileBroker::assign_compile_id() は JIT 要求を受け付けられない印として 0 を返すことがある.
        この場合は JIT コンパイルはしてはいけないので, ここでリターン)
        ---------------------------------------- -}

        // We now know that this compilation is not pending, complete,
        // or prohibited.  Assign a compile_id to this compilation
        // and check to see if it is in our [Start..Stop) range.
        uint compile_id = assign_compile_id(method, osr_bci);
        if (compile_id == 0) {
          // The compilation falls outside the allowed range.
          return;
        }

    {- -------------------------------------------
  (1.1) (変数宣言など)
        ---------------------------------------- -}

        // Should this thread wait for completion of the compile?
        blocking = is_compile_blocking(method, osr_bci);

    {- -------------------------------------------
  (1.1) CompileBroker::create_compile_task() を呼んで,
        CompilerQueue オブジェクトに JIT コンパイル要求を入れる
        (これにより CompilerThread に JIT コンパイル要求が通知され, JIT コンパイル処理が開始される)

        (なおコメントによると, )
        ---------------------------------------- -}

        // We will enter the compilation in the queue.
        // 14012000: Note that this sets the queued_for_compile bits in
        // the target method. We can now reason that a method cannot be
        // queued for compilation more than once, as follows:
        // Before a thread queues a task for compilation, it first acquires
        // the compile queue lock, then checks if the method's queued bits
        // are set or it has already been compiled. Thus there can not be two
        // instances of a compilation task for the same method on the
        // compilation queue. Consider now the case where the compilation
        // thread has already removed a task for that method from the queue
        // and is in the midst of compiling it. In this case, the
        // queued_for_compile bits must be set in the method (and these
        // will be visible to the current thread, since the bits were set
        // under protection of the compile queue lock, which we hold now.
        // When the compilation completes, the compiler thread first sets
        // the compilation result and then clears the queued_for_compile
        // bits. Neither of these actions are protected by a barrier (or done
        // under the protection of a lock), so the only guarantee we have
        // (on machines with TSO (Total Store Order)) is that these values
        // will update in that order. As a result, the only combinations of
        // these bits that the current thread will see are, in temporal order:
        // <RESULT, QUEUE> :
        //     <0, 1> : in compile queue, but not yet compiled
        //     <1, 1> : compiled but queue bit not cleared
        //     <1, 0> : compiled and queue bit cleared
        // Because we first check the queue bits then check the result bits,
        // we are assured that we cannot introduce a duplicate task.
        // Note that if we did the tests in the reverse order (i.e. check
        // result then check queued bit), we could get the result bit before
        // the compilation completed, and the queue bit after the compilation
        // completed, and end up introducing a "duplicate" (redundant) task.
        // In that case, the compiler thread should first check if a method
        // has already been compiled before trying to compile it.
        // NOTE: in the event that there are multiple compiler threads and
        // there is de-optimization/recompilation, things will get hairy,
        // and in that case it's best to protect both the testing (here) of
        // these bits, and their updating (here and elsewhere) under a
        // common lock.
        task = create_compile_task(queue,
                                   compile_id, method,
                                   osr_bci, comp_level,
                                   hot_method, hot_count, comment,
                                   blocking);

  {- -------------------------------------------
  (1) ここまでが compile_queue (CompileQueue オブジェクト) のロックで守られたクリティカルセクション
      ---------------------------------------- -}

      }

  {- -------------------------------------------
  (1) もし, 要求を出したスレッドは JIT コンパイル処理が終わるまで
      待機する設定になっている場合は,
      CompileBroker::wait_for_completion() を呼んで待機する.

      ("-Xbatch" コマンドラインオプションが付いている場合などにこうなる.
       See: CompileBroker::is_compile_blocking())
      ---------------------------------------- -}

      if (blocking) {
        wait_for_completion(task);
      }
    }

This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.