Top


定義場所(file name)

hotspot/src/share/vm/memory/referenceProcessor.cpp

説明(description)

なお, ReferenceProcessor::discover_reference() での扱い方には 2種類のポリシーがあり得るとのこと.

このポリシーのどちらを使用するかは, RefDiscoveryPolicy コマンドラインオプションで指定できる模様.

// We mention two of several possible choices here:
// #0: if the reference object is not in the "originating generation"
//     (or part of the heap being collected, indicated by our "span"
//     we don't treat it specially (i.e. we scan it as we would
//     a normal oop, treating its references as strong references).
//     This means that references can't be enqueued unless their
//     referent is also in the same span. This is the simplest,
//     most "local" and most conservative approach, albeit one
//     that may cause weak references to be enqueued least promptly.
//     We call this choice the "ReferenceBasedDiscovery" policy.
// #1: the reference object may be in any generation (span), but if
//     the referent is in the generation (span) being currently collected
//     then we can discover the reference object, provided
//     the object has not already been discovered by
//     a different concurrently running collector (as may be the
//     case, for instance, if the reference object is in CMS and
//     the referent in DefNewGeneration), and provided the processing
//     of this reference object by the current collector will
//     appear atomic to every other collector in the system.
//     (Thus, for instance, a concurrent collector may not
//     discover references in other generations even if the
//     referent is in its own generation). This policy may,
//     in certain cases, enqueue references somewhat sooner than
//     might Policy #0 above, but at marginally increased cost
//     and complexity in processing these references.
//     We call this choice the "RefeferentBasedDiscovery" policy.

名前(function name)

bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) {

本体部(body)

  {- -------------------------------------------
  (1) もし discovery 処理が停止されているか (= _discovering_refs フィールドが false になっているか), 
      または RegisterReferences オプションが指定されていない場合には, 
      特に処理は行わずにここでリターン.
      (See: ReferenceProcessor::disable_discovery())
      ---------------------------------------- -}

      // We enqueue references only if we are discovering refs
      // (rather than processing discovered refs).
      if (!_discovering_refs || !RegisterReferences) {
        return false;
      }

  {- -------------------------------------------
  (1) もし処理対象の参照オブジェクトの next フィールドが 
      null でなければ (= 参照オブジェクトが Active 状態でなければ)
      特別扱いする必要は無いので, ここでリターン.
      (See: java.lang.ref.Reference)
      ---------------------------------------- -}

      // We only enqueue active references.
      oop next = java_lang_ref_Reference::next(obj);
      if (next != NULL) {
        return false;
      }

  {- -------------------------------------------
  (1) もしポリシーが ReferenceBasedDiscovery の場合, 
      参照オブジェクトが処理対象の領域内(span 内)になければ
      ここでリターン.
      ---------------------------------------- -}

      HeapWord* obj_addr = (HeapWord*)obj;
      if (RefDiscoveryPolicy == ReferenceBasedDiscovery &&
          !_span.contains(obj_addr)) {
        // Reference is not in the originating generation;
        // don't treat it specially (i.e. we want to scan it as a normal
        // object with strong references).
        return false;
      }

  {- -------------------------------------------
  (1) もし, 処理対象の参照オブジェクトの差し先が
      他の強参照から到達可能であれば
      (つまり live であることが確定していれば)
      何もする必要は無いので, ここでリターン.

      (強参照から到達可能かどうかは, 
       引数で渡されたクロージャー(以下の is_alive_non_header)により
       差し先のオブジェクトの生死を調べることで行っている.)
      ---------------------------------------- -}

      // We only enqueue references whose referents are not (yet) strongly
      // reachable.
      if (is_alive_non_header() != NULL) {
        verify_referent(obj);
        if (is_alive_non_header()->do_object_b(java_lang_ref_Reference::referent(obj))) {
          return false;  // referent is reachable
        }
      }

  {- -------------------------------------------
  (1) もし処理対象の参照オブジェクトが Soft reference の場合は, 
      ReferencePolicy を使って, 差し先を消去すべきかどうかを判定してみる.
      もし消去しなくていいなら, 普通に強参照として扱えばいいだけなので, ここでリターン.

      (使用予定の ReferencePolicy オブジェクト (以下の _current_soft_ref_policy) について, 
       ReferencePolicy::should_clear_reference() で判定を行う.
       false を返したら差し先は消さなくてよい, ということを意味する.)
      ---------------------------------------- -}

      if (rt == REF_SOFT) {
        // For soft refs we can decide now if these are not
        // current candidates for clearing, in which case we
        // can mark through them now, rather than delaying that
        // to the reference-processing phase. Since all current
        // time-stamp policies advance the soft-ref clock only
        // at a major collection cycle, this is always currently
        // accurate.
        if (!_current_soft_ref_policy->should_clear_reference(obj)) {
          return false;
        }
      }

  {- -------------------------------------------
  (1) もし処理対象の参照オブジェクトが既に誰かに見つかっていたら, 
      これ以上処理することはないので, ここでリターン.
        (参照オブジェクトの discovered フィールドが NULL でなければ
         誰かが既に処理済み, ということを表す)

      なお, (普通は GC 中に同じオブジェクトを2回以上処理することはないはずなので) 
      これがどういうことかと言うと, 以下のような状況とのこと.
       * ポリシーが ReferentBasedDiscovery の場合:
         他の世代に対する GC 中に見つかっていた, というケース.
       * ポリシーが ReferenceBasedDiscovery の場合:
         Concurrent に動作していた GC スレッドが見つけていた, というケース (というわけで, CMS か G1GC でないと起こりえない).

      (ついでに, このケースでリターンする場合は
       リターン直前に(トレース出力)もしている)
      ---------------------------------------- -}

      HeapWord* const discovered_addr = java_lang_ref_Reference::discovered_addr(obj);
      const oop  discovered = java_lang_ref_Reference::discovered(obj);
      assert(discovered->is_oop_or_null(), "bad discovered field");
      if (discovered != NULL) {
        // The reference has already been discovered...

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

        if (TraceReferenceGC) {
          gclog_or_tty->print_cr("Already enqueued reference (" INTPTR_FORMAT ": %s)",
                                 obj, obj->blueprint()->internal_name());
        }
        if (RefDiscoveryPolicy == ReferentBasedDiscovery) {
          // assumes that an object is not processed twice;
          // if it's been already discovered it must be on another
          // generation's discovered list; so we won't discover it.
          return false;
        } else {
          assert(RefDiscoveryPolicy == ReferenceBasedDiscovery,
                 "Unrecognized policy");
          // Check assumption that an object is not potentially
          // discovered twice except by concurrent collectors that potentially
          // trace the same Reference object twice.
          assert(UseConcMarkSweepGC || UseG1GC,
                 "Only possible with a concurrent marking collector");
          return true;
        }
      }

  {- -------------------------------------------
  (1) ポリシーが ReferentBasedDiscovery の場合でも, 
      以下のどちらかが成り立っていないと, ここでリターン
        * 参照オブジェクトが, 処理対象の領域内(span 内)にある
        * この discovery 処理がアトミックに行われており (= CMS や G1GC のように並行して動いている GC スレッドがいない), かつ
          参照オブジェクトの指し先が処理対象の領域内(span 内)にある
      (なお, ポリシーが ReferenceBasedDiscovery の場合には, 
       一つ目の「参照オブジェクトが処理対象の領域内(span 内)にある」という条件が満たされなければリターン.
       See above.)
      ---------------------------------------- -}

      if (RefDiscoveryPolicy == ReferentBasedDiscovery) {
        verify_referent(obj);
        // enqueue if and only if either:
        // reference is in our span or
        // we are an atomic collector and referent is in our span
        if (_span.contains(obj_addr) ||
            (discovery_is_atomic() &&
             _span.contains(java_lang_ref_Reference::referent(obj)))) {
          // should_enqueue = true;
        } else {
          return false;
        }
      } else {
        assert(RefDiscoveryPolicy == ReferenceBasedDiscovery &&
               _span.contains(obj_addr), "code inconsistency");
      }

  {- -------------------------------------------
  (1) ReferenceProcessor::get_discovered_list() で, 
      処理対象の参照オブジェクトをつなぐための適切な DiscoveredList を取得する.

      (なお, ReferenceProcessor::get_discovered_list() が NULL を返したら
       ここでリターンすることになっている.

       ただし, NULL になるのは種別が REF_OTHER の場合だけで, 
       これは java.lang.ref.Reference のサブクラスであって 
       soft/weak/phantom/final のいずれでもないもの, の場合.

       <= JavaSE の標準クラスにはそんなものはない(??)ので, 
          自分で Reference のサブクラスを作った場合のみありうる?? #TODO)
      ---------------------------------------- -}

      // Get the right type of discovered queue head.
      DiscoveredList* list = get_discovered_list(rt);
      if (list == NULL) {
        return false;   // nothing special needs to be done
      }

  {- -------------------------------------------
  (1) (以下の if 文内で, 処理対象の参照オブジェクトを DiscoveredList につなぐ作業を行う.

       なお, discover 処理がマルチスレッドで行われているか(_discovery_is_mt が true かどうか)に応じて
       2通りに分岐しており, マルチスレッド処理の場合には
       実際の処理は ReferenceProcessor::add_to_discovered_list_mt() の中で行われる.

       また, コンストラクタ引数で 
       write barrier の実行が要求されている場合(_discovered_list_needs_barrier が true の場合)には, 
       参照オブジェクトの discovered フィールドに他の参照オブジェクトへのポインタを書き込む際に
       あわせて barrier set の更新処理も行う.)
      ---------------------------------------- -}

      if (_discovery_is_mt) {
        add_to_discovered_list_mt(*list, obj, discovered_addr);
      } else {
        // If "_discovered_list_needs_barrier", we do write barriers when
        // updating the discovered reference list.  Otherwise, we do a raw store
        // here: the field will be visited later when processing the discovered
        // references.
        oop current_head = list->head();

    {- -------------------------------------------
  (1.1) write barrier の実行が要求されている場合(_discovered_list_needs_barrier が true の場合)であっても
        G1GC の場合は BarrierSet::write_ref_field_pre() の実行までは必要ない.
        (というのは, 書き込む前の discovered フィールドの値が NULL だから)

        ReferenceProcessor::add_to_discovered_list_mt() 内のコメントによると, 
        「将来の GC アルゴリズムで pre-barrier を必要とするものが出てくるかもしれないので, この if 文を書いてある」とのこと.
        (現状だと, そもそも _discovered_list_needs_barrier が true になるのが G1GC の場合しかないような...)
        ---------------------------------------- -}

        // As in the case further above, since we are over-writing a NULL
        // pre-value, we can safely elide the pre-barrier here for the case of G1.
        assert(discovered == NULL, "control point invariant");
        if (_discovered_list_needs_barrier && !UseG1GC) { // safe to elide for G1
          if (UseCompressedOops) {
            _bs->write_ref_field_pre((narrowOop*)discovered_addr, current_head);
          } else {
            _bs->write_ref_field_pre((oop*)discovered_addr, current_head);
          }
          guarantee(false, "Need to check non-G1 collector");
        }

    {- -------------------------------------------
  (1.1) 処理対象の参照オブジェクトの discovered フィールドに, 
        現在リストの先頭になっている参照オブジェクトを書き込む
        ---------------------------------------- -}

        oop_store_raw(discovered_addr, current_head);

    {- -------------------------------------------
  (1.1) write barrier の実行が要求されている場合(_discovered_list_needs_barrier が true の場合)には
        BarrierSet::write_ref_field() を呼び出して barrier set を書き換えておく.
        ---------------------------------------- -}

        if (_discovered_list_needs_barrier) {
          _bs->write_ref_field((void*)discovered_addr, current_head);
        }

    {- -------------------------------------------
  (1.1) リストの先頭を, 今回処理した参照オブジェクトに置き換える.
        (ついでに, リストの長さをインクリメントしておく)
        ---------------------------------------- -}

        list->set_head(obj);
        list->inc_length(1);

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

        if (TraceReferenceGC) {
          gclog_or_tty->print_cr("Enqueued reference (" INTPTR_FORMAT ": %s)",
                                    obj, obj->blueprint()->internal_name());
        }
      }

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

      assert(obj->is_oop(), "Enqueued a bad reference");

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

      verify_referent(obj);

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

      return true;
    }

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