Top


定義場所(file name)

hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp

説明(description)

//
// This method is pretty bulky. It would be nice to split it up
// into smaller submethods, but we need to be careful not to hurt
// performance.
//

名前(function name)

oop PSPromotionManager::copy_to_survivor_space(oop o) {

本体部(body)

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

      assert(PSScavenge::should_scavenge(&o), "Sanity");

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

      oop new_obj = NULL;

  {- -------------------------------------------
  (1) (変数宣言など)
      (オブジェクトの mark フィールドは, GC 中は複数のスレッドから同時にアクセスされうるので
       oop のメソッドを使ってアクセスしないように, とのこと)
      ---------------------------------------- -}

      // NOTE! We must be very careful with any methods that access the mark
      // in o. There may be multiple threads racing on it, and it may be forwarded
      // at any time. Do not use oop methods for accessing the mark!
      markOop test_mark = o->mark();

  {- -------------------------------------------
  (1) もし処理対象のオブジェクトが未だコピーされていなければ, 
      以下のブロック内(if の then 側)の処理を行う.
      ---------------------------------------- -}

      // The same test as "o->is_forwarded()"
      if (!test_mark->is_marked()) {

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

        bool new_obj_is_tenured = false;
        size_t new_obj_size = o->size();

    {- -------------------------------------------
  (1.1) MT safe な方法でオブジェクトの年齢を取得する.
        ---------------------------------------- -}

        // Find the objects age, MT safe.
        int age = (test_mark->has_displaced_mark_helper() /* o->has_displaced_mark() */) ?
          test_mark->displaced_mark_helper()->age() : test_mark->age();

    {- -------------------------------------------
  (1.1) オブジェクトの年齢が Old 領域への昇格閾値(= PSScavenge::tenuring_threshold())より低い場合には, 
        PSYoungPromotionLAB::allocate() で To 領域内にコピー先を確保する.

        なお, PSYoungPromotionLAB::allocate() が失敗した場合, 
        まだ To 領域に空きがあれば以下の処理を行う. 
        * 処理対象のオブジェクトの大きさが YoungPLABSize/2 を超えているなら, 
          MutableSpace::cas_allocate() (または MutableNUMASpace::cas_allocate()) で
          直接 To 領域内に確保を試みる.
        * そうでなければ, To 領域から新しい PSYoungPromotionLAB を確保する.
          (PSPromotionLAB::flush() で現在使っている PSYoungPromotionLab の残りのスペースを dummy 配列で埋めた後, 
           MutableSpace::cas_allocate() (または MutableNUMASpace::cas_allocate()) で新しい PSYoungPromotionLab 用の領域を確保する.
           確保が成功すれば, 改めて PSYoungPromotionLAB::allocate() により To 領域内にコピー先を確保する.

           なお新しい PSYoungPromotionLab の確保に失敗した場合には, 今後も新たな PSYoungPromotionLab 用の領域は確保できないので, 
           無駄に確保を試みないように _young_gen_is_full フィールドを true にしている.)
        ---------------------------------------- -}

        // Try allocating obj in to-space (unless too old)
        if (age < PSScavenge::tenuring_threshold()) {
          new_obj = (oop) _young_lab.allocate(new_obj_size);
          if (new_obj == NULL && !_young_gen_is_full) {
            // Do we allocate directly, or flush and refill?
            if (new_obj_size > (YoungPLABSize / 2)) {
              // Allocate this object directly
              new_obj = (oop)young_space()->cas_allocate(new_obj_size);
            } else {
              // Flush and fill
              _young_lab.flush();

              HeapWord* lab_base = young_space()->cas_allocate(YoungPLABSize);
              if (lab_base != NULL) {
                _young_lab.initialize(MemRegion(lab_base, YoungPLABSize));
                // Try the young lab allocation again.
                new_obj = (oop) _young_lab.allocate(new_obj_size);
              } else {
                _young_gen_is_full = true;
              }
            }
          }
        }

    {- -------------------------------------------
  (1.1) オブジェクトの年齢が Old 領域への昇格閾値より高い場合, 
        もしくは To 領域内にメモリを確保するのに失敗した場合には, 
        PSOldPromotionLAB::allocate() で Old 領域内にコピー先を確保する.

        なお, PSOldPromotionLAB::allocate() が失敗した場合, 
        まだ Old 領域に空きがあれば以下の処理を行う. 
        * 処理対象のオブジェクトの大きさが OldPLABSize/2 を超えているなら, 
          PSOldGen::cas_allocate() で 直接 Old 領域内に確保を試みる.
        * そうでなければ, Old 領域から新しい PSOldPromotionLAB を確保する.
          (PSOldPromotionLAB::flush() で現在使っている PSOldPromotionLAB の残りのスペースを dummy 配列で埋めた後, 
           PSOldGen::cas_allocate() で新しい PSOldPromotionLAB 用の領域を確保する.
           確保が成功すれば, 改めて PSOldPromotionLAB::allocate() により Old 領域内にコピー先を確保する.

        以上の処理を行ってもコピー先の確保ができなかった場合には, 
        PSPromotionManager::oop_promotion_failed() で失敗したことを記録した後, 
        オブジェクト内のポインタを PSPromotionManager のタスクキューに追加して, ここでリターン.
          (ただし, この場合でも並列で動作している他のスレッドがコピーを成功させていた場合には, 
           確保が失敗したという記録は行わずそのままリターンする.)
        (なおこの確保失敗のケースでは, 後で PSScavenge::clean_up_failed_promotion() により後始末が行われる.
         See: PSScavenge::clean_up_failed_promotion())
        ---------------------------------------- -}

        // Otherwise try allocating obj tenured
        if (new_obj == NULL) {
    #ifndef PRODUCT
          if (Universe::heap()->promotion_should_fail()) {
            return oop_promotion_failed(o, test_mark);
          }
    #endif  // #ifndef PRODUCT

          new_obj = (oop) _old_lab.allocate(new_obj_size);
          new_obj_is_tenured = true;

          if (new_obj == NULL) {
            if (!_old_gen_is_full) {
              // Do we allocate directly, or flush and refill?
              if (new_obj_size > (OldPLABSize / 2)) {
                // Allocate this object directly
                new_obj = (oop)old_gen()->cas_allocate(new_obj_size);
              } else {
                // Flush and fill
                _old_lab.flush();

                HeapWord* lab_base = old_gen()->cas_allocate(OldPLABSize);
                if(lab_base != NULL) {
                  _old_lab.initialize(MemRegion(lab_base, OldPLABSize));
                  // Try the old lab allocation again.
                  new_obj = (oop) _old_lab.allocate(new_obj_size);
                }
              }
            }

            // This is the promotion failed test, and code handling.
            // The code belongs here for two reasons. It is slightly
            // different thatn the code below, and cannot share the
            // CAS testing code. Keeping the code here also minimizes
            // the impact on the common case fast path code.

            if (new_obj == NULL) {
              _old_gen_is_full = true;
              return oop_promotion_failed(o, test_mark);
            }
          }
        }

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

        assert(new_obj != NULL, "allocation should have succeeded");

    {- -------------------------------------------
  (1.1) オブジェクトの中身をコピーの領域にコピーする
        ---------------------------------------- -}

        // Copy obj
        Copy::aligned_disjoint_words((HeapWord*)o, (HeapWord*)new_obj, new_obj_size);

    {- -------------------------------------------
  (1.1) コピーが終わったので, コピー元のオブジェクトの mark フィールドに 
        CAS で forwarding pointer(コピー先のメモリアドレス) を埋め込む.
        ---------------------------------------- -}

        // Now we have to CAS in the header.
        if (o->cas_forward_to(new_obj, test_mark)) {

    {- -------------------------------------------
  (1.1) CAS が成功した場合は, (コピー先が Old 領域でなければ oopDesc::incr_age() でオブジェクトの年齢を増加させた後, )
        オブジェクト内のポインタを PSPromotionManager オブジェクト内に回収する.
        * コピーしたのが非常に大きい配列だった場合は, 
          mask_chunked_array_oop() で配列だと分かるように印を付けた後
          PSPromotionManager::push_depth() でタスクキューに追加する.
          (より正確には, PSChunkLargeArrays オプションが指定されており, かつ
           大きさが 1.5*ParGCArrayScanChunk よりも大きい配列オブジェクトの場合)
        * そうでなければ, 
          oopDesc::push_contents() でオブジェクト内のポインタをタスクキューに追加する.
        ---------------------------------------- -}

          // We won any races, we "own" this object.
          assert(new_obj == o->forwardee(), "Sanity");

          // Increment age if obj still in new generation. Now that
          // we're dealing with a markOop that cannot change, it is
          // okay to use the non mt safe oop methods.
          if (!new_obj_is_tenured) {
            new_obj->incr_age();
            assert(young_space()->contains(new_obj), "Attempt to push non-promoted obj");
          }

          // Do the size comparison first with new_obj_size, which we
          // already have. Hopefully, only a few objects are larger than
          // _min_array_size_for_chunking, and most of them will be arrays.
          // So, the is->objArray() test would be very infrequent.
          if (new_obj_size > _min_array_size_for_chunking &&
              new_obj->is_objArray() &&
              PSChunkLargeArrays) {
            // we'll chunk it
            oop* const masked_o = mask_chunked_array_oop(o);
            push_depth(masked_o);
            TASKQUEUE_STATS_ONLY(++_arrays_chunked; ++_masked_pushes);
          } else {
            // we'll just push its contents
            new_obj->push_contents(this);
          }

    {- -------------------------------------------
  (1.1) CAS が失敗した場合は, コピー先として確保していたメモリを PSPromotionLAB::unallocate_object() で deallocate する.

        (この場合は, 他の誰かがコピーしたということなので, forwardee() でコピー先を取得しておく.)
        (なお, 直接確保してしまっていた場合は deallocate できない.
         deallocate に失敗したら, しょうが無いので
         CollectedHeap::fill_with_object() でコピー先のメモリ領域を dummy 配列で埋めるだけにしておく.)
        ---------------------------------------- -}

        }  else {
          // We lost, someone else "owns" this object
          guarantee(o->is_forwarded(), "Object must be forwarded if the cas failed.");

          // Try to deallocate the space.  If it was directly allocated we cannot
          // deallocate it, so we have to test.  If the deallocation fails,
          // overwrite with a filler object.
          if (new_obj_is_tenured) {
            if (!_old_lab.unallocate_object(new_obj)) {
              CollectedHeap::fill_with_object((HeapWord*) new_obj, new_obj_size);
            }
          } else if (!_young_lab.unallocate_object(new_obj)) {
            CollectedHeap::fill_with_object((HeapWord*) new_obj, new_obj_size);
          }

          // don't update this before the unallocation!
          new_obj = o->forwardee();
        }

  {- -------------------------------------------
  (1) もし処理対象のオブジェクトが既にコピー済みであれば, 
      単にコピー先のアドレスを取ってくるだけ.
      ---------------------------------------- -}

      } else {
        assert(o->is_forwarded(), "Sanity");
        new_obj = o->forwardee();
      }

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

    #ifdef DEBUG
      // This code must come after the CAS test, or it will print incorrect
      // information.
      if (TraceScavenge) {
        gclog_or_tty->print_cr("&#x7b;&#x25;s %s " PTR_FORMAT " -> " PTR_FORMAT " (" SIZE_FORMAT ")}",
           PSScavenge::should_scavenge(&new_obj) ? "copying" : "tenuring",
           new_obj->blueprint()->internal_name(), o, new_obj, new_obj->size());
      }
    #endif

  {- -------------------------------------------
  (1) 結果をリターン
      ---------------------------------------- -}

      return new_obj;
    }

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