Top


定義場所(file name)

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

名前(function name)

bool
G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {

本体部(body)

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

      assert_at_safepoint(true /* should_be_vm_thread */);
      guarantee(!is_gc_active(), "collection is not reentrant");

  {- -------------------------------------------
  (1) もし GC_locker によって GC が禁止されていれば, ここでリターン.
      ---------------------------------------- -}

      if (GC_locker::check_active_before_gc()) {
        return false;
      }

  {- -------------------------------------------
  (1) (DTrace のフック点) (JVMTI のフック点)
      (See: SvcGCMarker)
      ---------------------------------------- -}

      SvcGCMarker sgcm(SvcGCMarker::MINOR);

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

      ResourceMark rm;

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

      if (PrintHeapAtGC) {
        Universe::print_heap_before_gc();
      }

  {- -------------------------------------------
  (1) (verify)
      ---------------------------------------- -}

      verify_region_sets_optional();
      verify_dirty_young_regions();

  {- -------------------------------------------
  (1) (以下のブロック内で実際の GC 処理が行われる)
      ---------------------------------------- -}

      {

  {- -------------------------------------------
  (1) G1CollectorPolicy::decide_on_conc_mark_initiation() を呼んで, 
      今回の Minor GC 後に Concurrent marking を行うかどうかを決める.
      ---------------------------------------- -}

        // This call will decide whether this pause is an initial-mark
        // pause. If it is, during_initial_mark_pause() will return true
        // for the duration of this pause.
        g1_policy()->decide_on_conc_mark_initiation();

  {- -------------------------------------------
  (1) (トレース出力用の処理) & (トレース出力)
      (#TODO increment_total_full_collections() もトレース出力用だっけ?)
      ---------------------------------------- -}

        char verbose_str[128];
        sprintf(verbose_str, "GC pause ");
        if (g1_policy()->in_young_gc_mode()) {
          if (g1_policy()->full_young_gcs())
            strcat(verbose_str, "(young)");
          else
            strcat(verbose_str, "(partial)");
        }
        if (g1_policy()->during_initial_mark_pause()) {
          strcat(verbose_str, " (initial-mark)");
          // We are about to start a marking cycle, so we increment the
          // full collection counter.
          increment_total_full_collections();
        }

        // if PrintGCDetails is on, we'll print long statistics information
        // in the collector policy code, so let's not print this as the output
        // is messy if we do.
        gclog_or_tty->date_stamp(PrintGC && PrintGCDateStamps);
        TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty);
        TraceTime t(verbose_str, PrintGC && !PrintGCDetails, true, gclog_or_tty);

        TraceCollectorStats tcs(g1mm()->incremental_collection_counters());

  {- -------------------------------------------
  (1) (トレース出力用の処理) (DTrace のフック点) (JMM のフック点)
      (See: TraceMemoryManagerStats)
      ---------------------------------------- -}

        TraceMemoryManagerStats tms(false /* fullGC */, gc_cause());

  {- -------------------------------------------
  (1) G1CollectedHeap::append_secondary_free_list_if_not_empty_with_lock() を呼んで, 
      SecondaryFreeRegionList の中身を MasterFreeRegionList に移動しておく.

      (なお, develop オプションである InlineWarmCalls がセットされている場合は何もしない)
      ---------------------------------------- -}

        // If the secondary_free_list is not empty, append it to the
        // free_list. No need to wait for the cleanup operation to finish;
        // the region allocation code will check the secondary_free_list
        // and wait if necessary. If the G1StressConcRegionFreeing flag is
        // set, skip this step so that the region allocation code has to
        // get entries from the secondary_free_list.
        if (!G1StressConcRegionFreeing) {
          append_secondary_free_list_if_not_empty_with_lock();
        }

  {- -------------------------------------------
  (1) #TODO (_gc_time_stamp を ++ しているだけだが...)
      ---------------------------------------- -}

        increment_gc_time_stamp();

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

        if (g1_policy()->in_young_gc_mode()) {
          assert(check_young_list_well_formed(),
                 "young list should be well formed");
        }

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

        { // Call to jvmpi::post_class_unload_events must occur outside of active GC
          IsGCActiveMark x;

  {- -------------------------------------------
  (1) G1CollectedHeap::gc_prologue() を呼んで, GC のための前準備を行っておく.
      ---------------------------------------- -}

          gc_prologue(false);

  {- -------------------------------------------
  (1) GC を行うことになったので, CollectedHeap::total_collections() の値を増加させておく.
      (See: CollectedHeap::total_collections())
      ---------------------------------------- -}

          increment_total_collections(false /* full gc */);

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

    #if G1_REM_SET_LOGGING
          gclog_or_tty->print_cr("\nJust chose CS, heap:");
          print();
    #endif

  {- -------------------------------------------
  (1) (verify)
      ---------------------------------------- -}

          if (VerifyBeforeGC && total_collections() >= VerifyGCStartAt) {
            HandleMark hm;  // Discard invalid handles created during verification
            gclog_or_tty->print(" VerifyBeforeGC:");
            prepare_for_verify();
            Universe::verify(false);
          }

  {- -------------------------------------------
  (1) #ifdef COMPILER2 の場合には, (これから GC を行うので) DerivedPointerTable の値をリセットしておく.
      (See: DerivedPointerTable)
      ---------------------------------------- -}

          COMPILER2_PRESENT(DerivedPointerTable::clear());

  {- -------------------------------------------
  (1) ReferenceProcessor が動いていた場合は, 一時的に停止しておく.

      (G1GC での参照オブジェクトの扱い方については 
      G1CollectedHeap::ref_processing_init() 中のコメントも参照, とのこと)
      ---------------------------------------- -}

          // Please see comment in G1CollectedHeap::ref_processing_init()
          // to see how reference processing currently works in G1.
          //
          // We want to turn off ref discovery, if necessary, and turn it back on
          // on again later if we do. XXX Dubious: why is discovery disabled?
          bool was_enabled = ref_processor()->discovery_enabled();
          if (was_enabled) ref_processor()->disable_discovery();

  {- -------------------------------------------
  (1) G1CollectedHeap::release_mutator_alloc_region() を呼んで, 
      確保に使用している HeapRegion (G1AllocRegion) を 
      GC に備えて retire させておく.
      ---------------------------------------- -}

          // Forget the current alloc region (we might even choose it to be part
          // of the collection set!).
          release_mutator_alloc_region();

  {- -------------------------------------------
  (1) (変数宣言など)
      (GC 開始時の時刻およびその際のメモリ使用量. すぐ後ろで g1_policy 内にこの値を記録するために使用する)
      ---------------------------------------- -}

          // The elapsed time induced by the start time below deliberately elides
          // the possible verification above.
          double start_time_sec = os::elapsedTime();
          size_t start_used_bytes = used();

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

    #if YOUNG_LIST_VERBOSE
          gclog_or_tty->print_cr("\nBefore recording pause start.\nYoung_list:");
          _young_list->print();
          g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty);
    #endif // YOUNG_LIST_VERBOSE

  {- -------------------------------------------
  (1) g1_policy 内に GC 開始時の時刻およびその際のメモリ使用量を記録する. (<= これって何に使われるんだっけ? #TODO)
      ---------------------------------------- -}

          g1_policy()->record_collection_pause_start(start_time_sec,
                                                     start_used_bytes);

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

    #if YOUNG_LIST_VERBOSE
          gclog_or_tty->print_cr("\nAfter recording pause start.\nYoung_list:");
          _young_list->print();
    #endif // YOUNG_LIST_VERBOSE

  {- -------------------------------------------
  (1) Concurrent Mark を開始する場合は, 
      ConcurrentMark::checkpointRootsInitialPre() を呼んで next TAMS をセットしておく.
      ---------------------------------------- -}

          if (g1_policy()->during_initial_mark_pause()) {
            concurrent_mark()->checkpointRootsInitialPre();
          }

  {- -------------------------------------------
  (1) G1CollectedHeap::save_marks() を呼んで, 
      全ての HeapRegion の top 位置を記録しておく.
      ---------------------------------------- -}

          save_marks();

  {- -------------------------------------------
  (1) concurrent marking が実行中の場合は, 
      ConcurrentMark::drainAllSATBBuffers() を呼んで
      残っている SATB バッファの処理を完了させておく.
      (SATB 方式なので, write barrier が退避した mark 開始時点での値の処理を全て終わらせないといけない)

      (ついでに, この処理に掛かった時間は g1_policy()->record_satb_drain_time() で記録される模様)
      ---------------------------------------- -}

          // We must do this before any possible evacuation that should propagate
          // marks.
          if (mark_in_progress()) {
            double start_time_sec = os::elapsedTime();

            _cm->drainAllSATBBuffers();
            double finish_mark_ms = (os::elapsedTime() - start_time_sec) * 1000.0;
            g1_policy()->record_satb_drain_time(finish_mark_ms);
          }

  {- -------------------------------------------
  (1) ConcurrentMark::set_oops_do_bound() を呼び出して, 
      この時点での mark stack と region stack の top 位置を記録しておく.
      (mark stack や region stack を処理する際には, この top 位置までしか見ないようにして最適化している.
       具体的には mark stack に対する oops_do() 処理と region stack に対する invalidate_entries_into_cset() 処理で行う最適化.
       ConcurrentMark::set_oops_do_bound() のコメント参照)
      ---------------------------------------- -}

          // Record the number of elements currently on the mark stack, so we
          // only iterate over these.  (Since evacuation may add to the mark
          // stack, doing more exposes race conditions.)  If no mark is in
          // progress, this will be zero.
          _cm->set_oops_do_bound();

  {- -------------------------------------------
  (1) #TODO (ConcurrentMark::newCSet() のコメントに書いてあるが grey なオブジェクトに関する最適化っぽい)
      ---------------------------------------- -}

          if (mark_in_progress()) {
            concurrent_mark()->newCSet();
          }

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

    #if YOUNG_LIST_VERBOSE
          gclog_or_tty->print_cr("\nBefore choosing collection set.\nYoung_list:");
          _young_list->print();
          g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty);
    #endif // YOUNG_LIST_VERBOSE

  {- -------------------------------------------
  (1) G1CollectorPolicy_BestRegionsFirst::choose_collection_set() を呼んで
      GC 処理の対象とする region を選択する.
      (指定された目標停止時間(以下の target_pause_time_ms) に基づいて決定.
       選んだ region は G1CollectorPolicy::_collection_set に格納.)
      ---------------------------------------- -}

          g1_policy()->choose_collection_set(target_pause_time_ms);

  {- -------------------------------------------
  (1) #TODO
      (GC 対象に選んだ region ...)
      ---------------------------------------- -}

          // We have chosen the complete collection set. If marking is
          // active then, we clear the region fields of any of the
          // concurrent marking tasks whose region fields point into
          // the collection set as these values will become stale. This
          // will cause the owning marking threads to claim a new region
          // when marking restarts.
          if (mark_in_progress()) {
            concurrent_mark()->reset_active_task_region_fields_in_cset();
          }

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

          // Nothing to do if we were unable to choose a collection set.
    #if G1_REM_SET_LOGGING
          gclog_or_tty->print_cr("\nAfter pause, heap:");
          print();
    #endif

  {- -------------------------------------------
  (1) GC 対象に選んだ全ての HeapRegion に PrepareForRSScanningClosure を適用し, 
      HeapRegionRemSet オブジェクトの _iter_claimed フィールドを 0 にリセットしておく.
      ---------------------------------------- -}

          PrepareForRSScanningClosure prepare_for_rs_scan;
          collection_set_iterate(&prepare_for_rs_scan);

  {- -------------------------------------------
  (1) G1CollectedHeap::setup_surviving_young_words() を呼び出して, ... #TODO
      (g1_policy()->young_cset_length() 分の大きさの配列を NEW_C_HEAP_ARRAY() で確保し, 
       _surviving_young_words フィールドにセットしている (ついでに配列の 0 クリアもしている)模様.
       new 領域内の live オブジェクト量に比例するようだが, これ何に使うの??)
      ---------------------------------------- -}

          setup_surviving_young_words();

  {- -------------------------------------------
  (1) G1CollectedHeap::get_gc_alloc_regions() を呼び出して, evacuation 先になる region を決定する.
      (G1CollectedHeap::get_gc_alloc_regions() 内部では, 
       前回の GC 時に使用していた region (_retained_gc_alloc_regions フィールドに入っている) がまだ使えるなら引き続きそれを使用し, 
       ダメなら新しく一つ region を選ぶ模様.
       See: G1CollectedHeap::get_gc_alloc_regions())
      ---------------------------------------- -}

          // Set up the gc allocation regions.
          get_gc_alloc_regions();

  {- -------------------------------------------
  (1) G1CollectedHeap::evacuate_collection_set() を呼んで, 
      選択した region に対して GC を実行する.
      ---------------------------------------- -}

          // Actually do the work...
          evacuate_collection_set();

  {- -------------------------------------------
  (1) 今回の GC で回収した HeapRegion をフリーリストにつなぐ.
      ---------------------------------------- -}

          free_collection_set(g1_policy()->collection_set());
          g1_policy()->clear_collection_set();

  {- -------------------------------------------
  (1) GC が終わったので, G1CollectedHeap::cleanup_surviving_young_words() を呼んで, 
      G1CollectedHeap::setup_surviving_young_words() で確保した配列を free しておく.
      ---------------------------------------- -}

          cleanup_surviving_young_words();

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

          // Start a new incremental collection set for the next pause.
          g1_policy()->start_incremental_cset_building();

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

          // Clear the _cset_fast_test bitmap in anticipation of adding
          // regions to the incremental collection set for the next
          // evacuation pause.
          clear_cset_fast_test();

  {- -------------------------------------------
  (1) #TODO
      ---------------------------------------- -}

          if (g1_policy()->in_young_gc_mode()) {

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

            _young_list->reset_sampled_info();

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

            // Don't check the whole heap at this point as the
            // GC alloc regions from this pause have been tagged
            // as survivors and moved on to the survivor list.
            // Survivor regions will fail the !is_young() check.
            assert(check_young_list_empty(false /* check_heap */),
                   "young list should be empty");

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

    #if YOUNG_LIST_VERBOSE
            gclog_or_tty->print_cr("Before recording survivors.\nYoung List:");
            _young_list->print();
    #endif // YOUNG_LIST_VERBOSE

    {- -------------------------------------------
  (1.1) G1CollectorPolicy オブジェクト内に survivor 領域の情報を記録しておく.
        (次回の G1CollectorPolicy::calculate_young_list_target_length() 時に使うため.
         (See: G1CollectorPolicy::calculate_young_list_target_length()))
        ---------------------------------------- -}

            g1_policy()->record_survivor_regions(_young_list->survivor_length(),
                                              _young_list->first_survivor_region(),
                                              _young_list->last_survivor_region());

    {- -------------------------------------------
  (1.1) survivor 領域の情報を incremental collection set 及び YoungList オブジェクト内で記録しておく(??)
        ---------------------------------------- -}

            _young_list->reset_auxilary_lists();
          }

  {- -------------------------------------------
  (1) _summary_bytes_used フィールドを更新しておく.
      ---------------------------------------- -}

          if (evacuation_failed()) {
            _summary_bytes_used = recalculate_used();
          } else {
            // The "used" of the the collection set have already been subtracted
            // when they were freed.  Add in the bytes evacuated.
            _summary_bytes_used += g1_policy()->bytes_in_to_space();
          }

  {- -------------------------------------------
  (1) 必要があれば Concurrent marking 処理を開始させる.
      ---------------------------------------- -}

          if (g1_policy()->in_young_gc_mode() &&
              g1_policy()->during_initial_mark_pause()) {
            concurrent_mark()->checkpointRootsInitialPost();
            set_marking_started();
            // CAUTION: after the doConcurrentMark() call below,
            // the concurrent marking thread(s) could be running
            // concurrently with us. Make sure that anything after
            // this point does not assume that we are the only GC thread
            // running. Note: of course, the actual marking work will
            // not start until the safepoint itself is released in
            // ConcurrentGCThread::safepoint_desynchronize().
            doConcurrentMark();
          }

  {- -------------------------------------------
  (1) (デバッグ用の処理) (See: G1CollectedHeap::allocate_dummy_regions())
      ---------------------------------------- -}

          allocate_dummy_regions();

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

    #if YOUNG_LIST_VERBOSE
          gclog_or_tty->print_cr("\nEnd of the pause.\nYoung_list:");
          _young_list->print();
          g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty);
    #endif // YOUNG_LIST_VERBOSE

  {- -------------------------------------------
  (1) G1CollectedHeap::init_mutator_alloc_region() を呼んで, 
      確保に使用している HeapRegion (G1AllocRegion) をセットしておく.
      ---------------------------------------- -}

          init_mutator_alloc_region();

  {- -------------------------------------------
  (1) 処理にかかった時間を g1_policy 内に記録
      ---------------------------------------- -}

          double end_time_sec = os::elapsedTime();
          double pause_time_ms = (end_time_sec - start_time_sec) * MILLIUNITS;
          g1_policy()->record_pause_time_ms(pause_time_ms);
          g1_policy()->record_collection_pause_end();

  {- -------------------------------------------
  (1) (プロファイル情報の記録)(JMM 用) (See: MemoryUsage)
      及び (JMM のフック点) でもある  (See: LowMemoryDetector)
      ---------------------------------------- -}

          MemoryService::track_memory_usage();

  {- -------------------------------------------
  (1) (verify)
      ---------------------------------------- -}

          if (VerifyAfterGC && total_collections() >= VerifyGCStartAt) {
            HandleMark hm;  // Discard invalid handles created during verification
            gclog_or_tty->print(" VerifyAfterGC:");
            prepare_for_verify();
            Universe::verify(false);
          }

  {- -------------------------------------------
  (1) GC 前に ReferenceProcessor が動いていた場合は, 再開させておく.
      ---------------------------------------- -}

          if (was_enabled) ref_processor()->enable_discovery();

  {- -------------------------------------------
  (1) 必要があれば, G1CollectedHeap::expand() を呼んでヒープ長を拡張しておく.
      ---------------------------------------- -}

          {
            size_t expand_bytes = g1_policy()->expansion_amount();
            if (expand_bytes > 0) {
              size_t bytes_before = capacity();
              if (!expand(expand_bytes)) {
                // We failed to expand the heap so let's verify that
                // committed/uncommitted amount match the backing store
                assert(capacity() == _g1_storage.committed_size(), "committed size mismatch");
                assert(max_capacity() == _g1_storage.reserved_size(), "reserved size mismatch");
              }
            }
          }

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

          if (mark_in_progress()) {
            concurrent_mark()->update_g1_committed();
          }

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

    #ifdef TRACESPINNING
          ParallelTaskTerminator::print_termination_counts();
    #endif

  {- -------------------------------------------
  (1) (assert) (See: G1CollectedHeap::gc_epilogue())
      ---------------------------------------- -}

          gc_epilogue(false);
        }

  {- -------------------------------------------
  (1) (デバッグ用の処理)
      (ExitAfterGCNum オプションの処理.
       ExitAfterGCNum が指定されている場合, total_collections() が ExitAfterGCNum 回数まで達していれば, ここで HotSpot の実行を終了する)
      ---------------------------------------- -}

        if (ExitAfterGCNum > 0 && total_collections() == ExitAfterGCNum) {
          gclog_or_tty->print_cr("Stopping after GC #%d", ExitAfterGCNum);
          print_tracing_info();
          vm_exit(-1);
        }
      }

  {- -------------------------------------------
  (1) (verify)
      ---------------------------------------- -}

      verify_region_sets_optional();

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

      TASKQUEUE_STATS_ONLY(if (ParallelGCVerbose) print_taskqueue_stats());
      TASKQUEUE_STATS_ONLY(reset_taskqueue_stats());

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

      if (PrintHeapAtGC) {
        Universe::print_heap_after_gc();
      }

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

      g1mm()->update_counters();

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

      if (G1SummarizeRSetStats &&
          (G1SummarizeRSetStatsPeriod > 0) &&
          (total_collections() % G1SummarizeRSetStatsPeriod == 0)) {
        g1_rem_set()->print_summary_info();
      }

  {- -------------------------------------------
  (1) リターン
      ---------------------------------------- -}

      return true;
    }

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