Top


定義場所(file name)

hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp

名前(function name)

void ConcurrentMarkThread::run() {

本体部(body)

  {- -------------------------------------------
  (1) ConcurrentGCThread::initialize_in_thread() を呼んで, 初期化を行う.
      ---------------------------------------- -}

      initialize_in_thread();

  {- -------------------------------------------
  (1) フィールドの初期化
      (処理の開始時間を記録しておく)
      ---------------------------------------- -}

      _vtime_start = os::elapsedVTime();

  {- -------------------------------------------
  (1) ConcurrentGCThread::wait_for_universe_init() を呼んで, 
      HotSpot の初期化が終わるまで(= is_init_completed() が true になるまで)待機する.
      ---------------------------------------- -}

      wait_for_universe_init();

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

      G1CollectedHeap* g1h = G1CollectedHeap::heap();
      G1CollectorPolicy* g1_policy = g1h->g1_policy();
      G1MMUTracker *mmu_tracker = g1_policy->mmu_tracker();
      Thread *current_thread = Thread::current();

  {- -------------------------------------------
  (1) (以下が ConcurrentMarkThread の処理のメインループ.
       HotSpot が終了するまで(= _should_terminate フィールドが true になるまで) この while ループを実行.
       See: ConcurrentMarkThread::stop())
      ---------------------------------------- -}

      while (!_should_terminate) {

    {- -------------------------------------------
  (1.1) ConcurrentMarkThread::sleepBeforeNextCycle() を呼んで, 
        G1CollectedHeap::doConcurrentMark() で起こされるまで待つ.
        (See: [here](no2935YzN.html) for details)
        ---------------------------------------- -}

        // wait until started is set.
        sleepBeforeNextCycle();
        {

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

          ResourceMark rm;
          HandleMark   hm;
          double cycle_start = os::elapsedVTime();
          double mark_start_sec = os::elapsedTime();
          char verbose_str[128];

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

          if (PrintGC) {
            gclog_or_tty->date_stamp(PrintGCDateStamps);
            gclog_or_tty->stamp(PrintGCTimeStamps);
            gclog_or_tty->print_cr("[GC concurrent-mark-start]");
          }

    {- -------------------------------------------
  (1.1) 使用するアルゴリズムが Non-Generational な G1GC の場合
        (= in_young_gc_mode() が false の場合
         = G1Gen オプションが指定されていない場合) は, 
        以下の if ブロック内で Initial marking pause 処理を行う.

        (なお, ... #TODO) 
        ---------------------------------------- -}

          if (!g1_policy->in_young_gc_mode()) {

      {- -------------------------------------------
  (1.1.1) 念のため, G1CollectedHeap::set_marking_complete() を呼び出しておく.
          ---------------------------------------- -}

            // this ensures the flag is not set if we bail out of the marking
            // cycle; normally the flag is cleared immediately after cleanup
            g1h->set_marking_complete();

      {- -------------------------------------------
  (1.1.1) ユーザー指定の GC 時間制約を守る場合には 
          (= G1CollectorPolicy::adaptive_young_list_length() が true の場合には), 
          次の GC 実行までに必要な待ち時間を G1MMUTracker::when_ms() で計算し, 
          その時間分だけ os::sleep() で待機する.
          ---------------------------------------- -}

            if (g1_policy->adaptive_young_list_length()) {
              double now = os::elapsedTime();
              double init_prediction_ms = g1_policy->predict_init_time_ms();
              jlong sleep_time_ms = mmu_tracker->when_ms(now, init_prediction_ms);
              os::sleep(current_thread, sleep_time_ms, false);
            }

      {- -------------------------------------------
  (1.1.1) CMCheckpointRootsInitialClosure で Initial marking pause 処理を実行する.
          ---------------------------------------- -}

            // We don't have to skip here if we've been asked to restart, because
            // in the worst case we just enqueue a new VM operation to start a
            // marking.  Note that the init operation resets has_aborted()
            CMCheckpointRootsInitialClosure init_cl(_cm);
            strcpy(verbose_str, "GC initial-mark");
            VM_CGC_Operation op(&init_cl, verbose_str);
            VMThread::execute(&op);
          }

    {- -------------------------------------------
  (1.1) (変数宣言など)
        (iter はトレース出力用の変数)
        ---------------------------------------- -}

          int iter = 0;

    {- -------------------------------------------
  (1.1) (以下の do...while ブロック内で concurrent marking 処理を行い, 
         最後に CMCheckpointRootsFinalClosure で Final marking pause 処理も行う.

         なお, 処理が失敗した場合は ConcurrentMark::restart_for_overflow() が true に変わる
         (See: ConcurrentMark::checkpointRootsFinal()).
         この場合は成功するまで処理を繰り返す.
         このために, ブロックの最後でこれをチェックして true だったらやり直し, という流れになっている.)
        ---------------------------------------- -}

          do {

      {- -------------------------------------------
  (1.1.1) (トレース出力用の処理)
          ---------------------------------------- -}

            iter++;

      {- -------------------------------------------
  (1.1.1) ConcurrentMark::markFromRoots() を呼んで, 
          root から辿れる範囲の Concurrent marking 処理を行う.

          (ただし, 並行して Full GC が実行されてしまった場合は
           (= ConcurrentMark::has_aborted() が true の場合は), 
           これ以上 marking 処理を続けても結果は不正なので, 何もしない)
          ---------------------------------------- -}

            if (!cm()->has_aborted()) {
              _cm->markFromRoots();
            }

      {- -------------------------------------------
  (1.1.1) (トレース出力用の処理)

          (_vtime_mark_accum は現在は以下のパスでのみ参照されている.

           before_exit()
           -> G1CollectedHeap::print_tracing_info()
              -> ConcurrentMark::print_summary_info() (G1SummarizeConcMark オプション指定時のみ)
                 -> ConcurrentMarkThread::vtime_mark_accum())
          ---------------------------------------- -}

            double mark_end_time = os::elapsedVTime();
            double mark_end_sec = os::elapsedTime();
            _vtime_mark_accum += (mark_end_time - cycle_start);

      {- -------------------------------------------
  (1.1.1) (以下の if ブロック内で Final marking pause を行う.
           ただし, 並行して Full GC が実行されてしまった場合は
           (= ConcurrentMark::has_aborted() が true の場合は), 
           これ以上 marking 処理を続けても結果は不正なので, 何もしない)
          ---------------------------------------- -}

            if (!cm()->has_aborted()) {

        {- -------------------------------------------
  (1.1.1.1) ユーザー指定の GC 時間制約を守る場合には 
            (= G1CollectorPolicy::adaptive_young_list_length() が true の場合には), 
            次の GC 実行までに必要な待ち時間を G1MMUTracker::when_ms() で計算し, 
            その時間分だけ os::sleep() で待機する.
            ---------------------------------------- -}

              if (g1_policy->adaptive_young_list_length()) {
                double now = os::elapsedTime();
                double remark_prediction_ms = g1_policy->predict_remark_time_ms();
                jlong sleep_time_ms = mmu_tracker->when_ms(now, remark_prediction_ms);
                os::sleep(current_thread, sleep_time_ms, false);
              }

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

              if (PrintGC) {
                gclog_or_tty->date_stamp(PrintGCDateStamps);
                gclog_or_tty->stamp(PrintGCTimeStamps);
                gclog_or_tty->print_cr("[GC concurrent-mark-end, %1.7lf sec]",
                                          mark_end_sec - mark_start_sec);
              }

        {- -------------------------------------------
  (1.1.1.1) CMCheckpointRootsFinalClosure で Final marking pause 処理を実行する.
            ---------------------------------------- -}

              CMCheckpointRootsFinalClosure final_cl(_cm);
              sprintf(verbose_str, "GC remark");
              VM_CGC_Operation op(&final_cl, verbose_str);
              VMThread::execute(&op);
            }

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

            if (cm()->restart_for_overflow() &&
                G1TraceMarkStackOverflow) {
              gclog_or_tty->print_cr("Restarting conc marking because of MS overflow "
                                     "in remark (restart #%d).", iter);
            }

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

            if (cm()->restart_for_overflow()) {
              if (PrintGC) {
                gclog_or_tty->date_stamp(PrintGCDateStamps);
                gclog_or_tty->stamp(PrintGCTimeStamps);
                gclog_or_tty->print_cr("[GC concurrent-mark-restart-for-overflow]");
              }
            }

    {- -------------------------------------------
  (1.1) (ConcurrentMark::restart_for_overflow() が true になっていたらやり直し)
        ---------------------------------------- -}

          } while (cm()->restart_for_overflow());

    {- -------------------------------------------
  (1.1) (変数宣言など)
        (counting_start_time はトレース出力用の変数)
        ---------------------------------------- -}

          double counting_start_time = os::elapsedVTime();

    {- -------------------------------------------
  (1.1) ?? #TODO
        ---------------------------------------- -}

          // YSR: These look dubious (i.e. redundant) !!! FIX ME
          slt()->manipulatePLL(SurrogateLockerThread::acquirePLL);
          slt()->manipulatePLL(SurrogateLockerThread::releaseAndNotifyPLL);

    {- -------------------------------------------
  (1.1) ConcurrentMark::calcDesiredRegions() を呼んで, 
        生きているオブジェクトの量を計算しておく.

        (ついでに, (トレース出力)も出している)

        (ただし, 並行して Full GC が実行されてしまった場合は
         (= ConcurrentMark::has_aborted() が true の場合は), 
         これ以上 marking 処理を続けても結果は不正なので, 何もしない)
        ---------------------------------------- -}

          if (!cm()->has_aborted()) {
            double count_start_sec = os::elapsedTime();
            if (PrintGC) {
              gclog_or_tty->date_stamp(PrintGCDateStamps);
              gclog_or_tty->stamp(PrintGCTimeStamps);
              gclog_or_tty->print_cr("[GC concurrent-count-start]");
            }

            _sts.join();
            _cm->calcDesiredRegions();
            _sts.leave();

            if (!cm()->has_aborted()) {
              double count_end_sec = os::elapsedTime();
              if (PrintGC) {
                gclog_or_tty->date_stamp(PrintGCDateStamps);
                gclog_or_tty->stamp(PrintGCTimeStamps);
                gclog_or_tty->print_cr("[GC concurrent-count-end, %1.7lf]",
                                       count_end_sec - count_start_sec);
              }
            }
          }

    {- -------------------------------------------
  (1.1) (トレース出力用の処理)

        (_vtime_count_accum は現在は以下のパスでのみ参照されている.

         before_exit()
         -> G1CollectedHeap::print_tracing_info()
            -> ConcurrentMark::print_summary_info() (G1SummarizeConcMark オプション指定時のみ)
               -> ConcurrentMarkThread::vtime_count_accum())
        ---------------------------------------- -}

          double end_time = os::elapsedVTime();
          _vtime_count_accum += (end_time - counting_start_time);

    {- -------------------------------------------
  (1.1) (トレース出力用の処理)

        (_vtime_accum は現在は以下のパスでのみ参照されている. ...)
        ---------------------------------------- -}

          // Update the total virtual time before doing this, since it will try
          // to measure it to get the vtime for this marking.  We purposely
          // neglect the presumably-short "completeCleanup" phase here.
          _vtime_accum = (end_time - _vtime_start);

    {- -------------------------------------------
  (1.1) (以下の if ブロック内で Live Data Counting & Cleanup を行う.

         ただし, 並行して Full GC が実行されてしまった場合は
         (= ConcurrentMark::has_aborted() が true の場合は), 
         これ以上 marking 処理を続けても結果は不正なので, 処理は行わない.
         代わりに, G1CollectedHeap::set_marking_complete() を呼んで 
         Concurrent Mark 処理が終了したことが分かるようにしておく.)
        ---------------------------------------- -}

          if (!cm()->has_aborted()) {

      {- -------------------------------------------
  (1.1.1) ユーザー指定の GC 時間制約を守る場合には 
          (= G1CollectorPolicy::adaptive_young_list_length() が true の場合には), 
          次の GC 実行までに必要な待ち時間を G1MMUTracker::when_ms() で計算し, 
          その時間分だけ os::sleep() で待機する.
          ---------------------------------------- -}

            if (g1_policy->adaptive_young_list_length()) {
              double now = os::elapsedTime();
              double cleanup_prediction_ms = g1_policy->predict_cleanup_time_ms();
              jlong sleep_time_ms = mmu_tracker->when_ms(now, cleanup_prediction_ms);
              os::sleep(current_thread, sleep_time_ms, false);
            }

      {- -------------------------------------------
  (1.1.1) CMCleanUp で Live Data Counting & Cleanup 処理を実行する.
          ---------------------------------------- -}

            CMCleanUp cl_cl(_cm);
            sprintf(verbose_str, "GC cleanup");
            VM_CGC_Operation op(&cl_cl, verbose_str);
            VMThread::execute(&op);

    {- -------------------------------------------
  (1.1) (以下は, 並行して Full GC が実行された場合)
         G1CollectedHeap::set_marking_complete() を呼んで 
         Concurrent Mark 処理が終了したことが分かるようにしておく.
        ---------------------------------------- -}

          } else {
            g1h->set_marking_complete();
          }

    {- -------------------------------------------
  (1.1) Cleanup 処理で空の HeapRegion が見つかっていた場合は
        (= G1CollectedHeap::free_regions_coming() が true の場合は), 
        以下の if ブロック内で処理を行う
        (See: ConcurrentMark::cleanup())
        ---------------------------------------- -}

          // Check if cleanup set the free_regions_coming flag. If it
          // hasn't, we can just skip the next step.
          if (g1h->free_regions_coming()) {
            // The following will finish freeing up any regions that we
            // found to be empty during cleanup. We'll do this part
            // without joining the suspendible set. If an evacuation pause
            // takes place, then we would carry on freeing regions in
            // case they are needed by the pause. If a Full GC takes
            // place, it would wait for us to process the regions
            // reclaimed by cleanup.

      {- -------------------------------------------
  (1.1.1) (変数宣言など)
          (cleanup_start_sec はトレース出力用の変数)
          ---------------------------------------- -}

            double cleanup_start_sec = os::elapsedTime();

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

            if (PrintGC) {
              gclog_or_tty->date_stamp(PrintGCDateStamps);
              gclog_or_tty->stamp(PrintGCTimeStamps);
              gclog_or_tty->print_cr("[GC concurrent-cleanup-start]");
            }

      {- -------------------------------------------
  (1.1.1) 
          ---------------------------------------- -}

            // Now do the remainder of the cleanup operation.
            _cm->completeCleanup();

      {- -------------------------------------------
  (1.1.1) free region 処理の完了を待っているスレッドがいるかもしれないので, 
    G1CollectedHeap::reset_free_regions_coming() を呼んで
          処理の完了を通知しておく.
          (See: G1CollectedHeap::wait_while_free_regions_coming())

          (なおコメントによると, 
     この処理は sts に join() する前に行わないとデッドロックの危険があるので注意, 
     とのこと)
          ---------------------------------------- -}

            // Notify anyone who's waiting that there are no more free
            // regions coming. We have to do this before we join the STS,
            // otherwise we might deadlock: a GC worker could be blocked
            // waiting for the notification whereas this thread will be
            // blocked for the pause to finish while it's trying to join
            // the STS, which is conditional on the GC workers finishing.
            g1h->reset_free_regions_coming();

      {- -------------------------------------------
  (1.1.1) 
          ---------------------------------------- -}

            _sts.join();
            g1_policy->record_concurrent_mark_cleanup_completed();
            _sts.leave();

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

            double cleanup_end_sec = os::elapsedTime();
            if (PrintGC) {
              gclog_or_tty->date_stamp(PrintGCDateStamps);
              gclog_or_tty->stamp(PrintGCTimeStamps);
              gclog_or_tty->print_cr("[GC concurrent-cleanup-end, %1.7lf]",
                                     cleanup_end_sec - cleanup_start_sec);
            }
          }

    {- -------------------------------------------
  (1.1) (assert)
        ---------------------------------------- -}

          guarantee(cm()->cleanup_list_is_empty(),
                    "at this point there should be no regions on the cleanup list");

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

          if (cm()->has_aborted()) {
            if (PrintGC) {
              gclog_or_tty->date_stamp(PrintGCDateStamps);
              gclog_or_tty->stamp(PrintGCTimeStamps);
              gclog_or_tty->print_cr("[GC concurrent-mark-abort]");
            }
          }

    {- -------------------------------------------
  (1.1) ConcurrentMark::clearNextBitmap() を呼んで, _nextMarkBitMap をクリアする.

        (なお, この処理は適宜 SuspendibleThreadSet::yield() しながら行うので, 
         この処理の間は SuspendibleThreadSet オブジェクト(_sts)にカレントスレッドを登録しておく)
        ---------------------------------------- -}

          // we now want to allow clearing of the marking bitmap to be
          // suspended by a collection pause.
          _sts.join();
          _cm->clearNextBitmap();
          _sts.leave();

    {- -------------------------------------------
  (1.1) (ここまでが ResourceMark や HandleMark のスコープ)
        ---------------------------------------- -}

        }

    {- -------------------------------------------
  (1.1) (Full GC の完了を待っているスレッドがいるかもしれないので)
        G1CollectedHeap::increment_full_collections_completed() を呼び出して
        待っているスレッドがいれば起床させておく.
        (See: VM_G1IncCollectionPause::doit_epilogue())
        ---------------------------------------- -}

        // Update the number of full collections that have been
        // completed. This will also notify the FullGCCount_lock in case a
        // Java thread is waiting for a full GC to happen (e.g., it
        // called System.gc() with +ExplicitGCInvokesConcurrent).
        _sts.join();
        g1h->increment_full_collections_completed(true /* concurrent */);
        _sts.leave();

  {- -------------------------------------------
  (1) (ここまでが ConcurrentMarkThread の処理のメインループ. _should_terminate が true の間は以上の処理を繰り返す)
      ---------------------------------------- -}

      }

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

      assert(_should_terminate, "just checking");

  {- -------------------------------------------
  (1) ConcurrentGCThread::terminate() を呼び出して, 
      ConcurrentMarkThread::stop() 内でこのスレッドの終了を待っているスレッドを起床させる.
      (See: ConcurrentMarkThread::stop())
      ---------------------------------------- -}

      terminate();
    }

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