Top


定義場所(file name)

hotspot/src/share/vm/memory/space.hpp

名前(function name)

#define SCAN_AND_FORWARD(cp,scan_limit,block_is_obj,block_size) {            \

本体部(body)

  {- -------------------------------------------
  (1) 領域の先頭(bottom)から指定の終端アドレス(引数の scan_limit) までを順に辿り, 
      markされている全てのオブジェクトに対して, 
      指定されたコンパクト先 (引数の cp(CompactPoint オブジェクト)) に
      コンパクションした際のアドレスを
      forwarding pointer として埋め込んでいく.

      (なお, 死んでいるオブジェクトには次のlive objectのアドレスを埋め込むことで,
       phase3/phase4の際に連続したdead領域を一気にスキップできるようにしている.
       See: LiveRange)

      (なお, 新しいアドレスを実際に計算しているのは CompactibleSpace::forward() メソッド)
      ---------------------------------------- -}

      /* Compute the new addresses for the live objects and store it in the mark \
       * Used by universe::mark_sweep_phase2()                                   \
       */                                                                        \
      HeapWord* compact_top; /* This is where we are currently compacting to. */ \
                                                                                 \

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

      /* We're sure to be here before any objects are compacted into this        \
       * space, so this is a good time to initialize this:                       \
       */                                                                        \
      set_compaction_top(bottom());                                              \
                                                                                 \

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

      if (cp->space == NULL) {                                                   \
        assert(cp->gen != NULL, "need a generation");                            \
        assert(cp->threshold == NULL, "just checking");                          \
        assert(cp->gen->first_compaction_space() == this, "just checking");      \
        cp->space = cp->gen->first_compaction_space();                           \
        compact_top = cp->space->bottom();                                       \
        cp->space->set_compaction_top(compact_top);                              \
        cp->threshold = cp->space->initialize_threshold();                       \
      } else {                                                                   \
        compact_top = cp->space->compaction_top();                               \
      }                                                                          \
                                                                                 \

  {- -------------------------------------------
  (1) 毎回全部コンパクションしていると遅いので, 普段は完全なコンパクションはしない.
      (より正確には, MarkSweepAlwaysCompactCount 回に 1 回しか完全なコンパクションは行わない)

      それ以外の場合(以下の skip_dead が true の場合)には, 
      領域の先頭にある程度の量のゴミが残っていてよいものとする.
      (この量を指定するのが, 以下の allowed_deadspace.
       allowed_deadspace は, コンパクション先の領域の大きさ(capacity)に, 
       TenuredSpace::allowed_dead_ratio() または 
       ContigPermSpace::allowed_dead_ratio() の値を掛けて計算する.
       See: MarkSweepDeadRatio, PermMarkSweepDeadRatio)
      ---------------------------------------- -}

      /* We allow some amount of garbage towards the bottom of the space, so     \
       * we don't start compacting before there is a significant gain to be made.\
       * Occasionally, we want to ensure a full compaction, which is determined  \
       * by the MarkSweepAlwaysCompactCount parameter.                           \
       */                                                                        \
      int invocations = SharedHeap::heap()->perm_gen()->stat_record()->invocations;\
      bool skip_dead = ((invocations % MarkSweepAlwaysCompactCount) != 0);       \
                                                                                 \
      size_t allowed_deadspace = 0;                                              \
      if (skip_dead) {                                                           \
        const size_t ratio = allowed_dead_ratio();                               \
        allowed_deadspace = (capacity() * ratio / 100) / HeapWordSize;           \
      }                                                                          \
                                                                                 \

  {- -------------------------------------------
  (1) (変数宣言など)
      (q は現在処理しているオブジェクトを指すポインタ.
       t は処理範囲の終端を示すポインタ.)
      ---------------------------------------- -}

      HeapWord* q = bottom();                                                    \
      HeapWord* t = scan_limit();                                                \
                                                                                 \
      HeapWord*  end_of_live= q;    /* One byte beyond the last byte of the last \
                                       live object. */                           \
      HeapWord*  first_dead = end();/* The first dead object. */                 \
      LiveRange* liveRange  = NULL; /* The current live range, recorded in the   \
                                       first header of preceding free area. */   \

  {- -------------------------------------------
  (1) first_dead の情報は phase3 や phase4 でも使うため, _first_dead フィールドに記録しておく.
      (下の方でもう一回やっているような気もするが... #TODO)
      ---------------------------------------- -}

      _first_dead = first_dead;                                                  \
                                                                                 \

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

      const intx interval = PrefetchScanIntervalInBytes;                         \
                                                                                 \

  {- -------------------------------------------
  (1) 処理範囲の終端(t)に到達するまで, 次の while ループを繰り返して, 処理を行っていく.
      このループ内では以下のような処理を行う.

      * 処理対象の箇所(q)のオブジェクトが生きている場合 (= is_gc_marked() が true の場合):
        oopDesc::forward_to() で, そのオブジェクトにコンパクション先を指す forwarding pointer を埋める.
        その後, そのオブジェクトの大きさ分だけポインタ(q)を前に進める.
        (これにより, ループの次の週では, 次のオブジェクトに対する処理が始まる)

        (なお, 移動先が現在と同じアドレスの場合(= つまり, 移動しない場合)には, 
         そのオブジェクトの mark フィールドを初期状態の値にリセットするだけにしている.
         この情報は後で使用する.
         See: PSMarkSweepDecorator::adjust_pointers())

        (また, コンパクション先の領域に空き容量が足りない場合には, 
         コンパクション先を別の領域に切り替える処理も行っている)

      * dead オブジェクトに到達した場合 (= is_gc_marked() が false の場合):
        ゴミの量が allowed_deadspace に達するまでは, 
        たとえ死んでいても生きているオブジェクトと同様に扱う.
        (この場合, 上の生きている場合の処理とほぼ同じ)

        allowed_deadspace に達した後は,
        適切な LiveRange オブジェクトをその場に埋めた後, 
        次の live オブジェクトの位置までポインタ(q)を進める, という処理を行う.
        (これにより, dead オブジェクトばかりが続く領域は一気にスキップし, 
         ループの次の週では次の live オブジェクトに対する処理が始まる)

      (なお高速化のため, Prefetch::write() で 
       PrefetchScanIntervalInBytes バイトずつ
       メモリをプリフェッチしながら行う)
      ---------------------------------------- -}

      while (q < t) {                                                            \

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

        assert(!block_is_obj(q) ||                                               \
               oop(q)->mark()->is_marked() || oop(q)->mark()->is_unlocked() ||   \
               oop(q)->mark()->has_bias_pattern(),                               \
               "these are the only valid states during a mark sweep");           \

    {- -------------------------------------------
  (1.1) 現在箇所のオブジェクトが生きている場合には, 
        CompactibleSpace::forward() で新しいアドレスを指す forwarding pointer を埋め込む.
        その後, ポインタ(q)をそのオブジェクトのサイズ分だけ進める.
        (ついでに, end_of_live 変数の値も q に変更している)
        ---------------------------------------- -}

        if (block_is_obj(q) && oop(q)->is_gc_marked()) {                         \
          /* prefetch beyond q */                                                \
          Prefetch::write(q, interval);                                          \
          /* size_t size = oop(q)->size();  changing this for cms for perm gen */\
          size_t size = block_size(q);                                           \
          compact_top = cp->space->forward(oop(q), size, cp, compact_top);       \
          q += size;                                                             \
          end_of_live = q;                                                       \

    {- -------------------------------------------
  (1.1) 現在箇所のオブジェクトが死んでいる場合には, 以下の else ブロック内の処理を行う.
        ---------------------------------------- -}

        } else {                                                                 \

      {- -------------------------------------------
  (1.1.1) 次の live オブジェクトの位置を探す
          (以下の end に次の live オブジェクトの先頭アドレスが入る)
          ---------------------------------------- -}

          /* run over all the contiguous dead objects */                         \
          HeapWord* end = q;                                                     \
          do {                                                                   \
            /* prefetch beyond end */                                            \
            Prefetch::write(end, interval);                                      \
            end += block_size(end);                                              \
          } while (end < t && (!block_is_obj(end) || !oop(end)->is_gc_marked()));\
                                                                                 \

      {- -------------------------------------------
  (1.1.1) もし, ゴミの量が allowed_deadspace 量まで達していない場合には, 
          以下の if ブロック内の処理を行う.

          この場合には, 実際にはゴミオブジェクトであるが, live なオブジェクトとして扱う.
          (より正確には, 現在箇所(q)から次の live オブジェクト位置(end)までを
           1つの live オブジェクトが占めていると考え, 
           それに対して forwarding pointer を埋め込む, という処理を行う)

          この場合, if ブロックの最後に continue があるため, 
          if 以降の文は実行することなく, このままループの次の周へと進む.

          (allowed_deadspace 量まで達したかどうかは
           CompactibleSpace::insert_deadspace() で判定する.

           CompactibleSpace::insert_deadspace() は, 
           ゴミの量が allowed_deadspace 量に達するまでは
           コンパクション先に指定されたゴミオブジェクト領域と同サイズのダミーオブジェクトを生成して, true を返す.
           ゴミの量が allowed_deadspace 量に達して以降は, 単に false を返すだけ.

           なお, 正確には CompactibleSpace::insert_deadspace() は引数が参照渡しになっているため, 
           呼び出す度に allowed_deadspace の値が減少していく.
           ゴミの量が当初の allowed_deadspace 量に達した時点で, allowed_deadspace の値は 0 になる)
          ---------------------------------------- -}

          /* see if we might want to pretend this object is alive so that        \
           * we don't have to compact quite as often.                            \
           */                                                                    \
          if (allowed_deadspace > 0 && q == compact_top) {                       \
            size_t sz = pointer_delta(end, q);                                   \
            if (insert_deadspace(allowed_deadspace, q, sz)) {                    \
              compact_top = cp->space->forward(oop(q), sz, cp, compact_top);     \
              q = end;                                                           \
              end_of_live = end;                                                 \
              continue;                                                          \
            }                                                                    \
          }                                                                      \
                                                                                 \
          /* otherwise, it really is a free region. */                           \
                                                                                 \

      {- -------------------------------------------
  (1.1.1) 一つ前の LiveRange の終端を, 今回見つけた dead 領域の先頭とする.
          ---------------------------------------- -}

          /* for the previous LiveRange, record the end of the live objects. */  \
          if (liveRange) {                                                       \
            liveRange->set_end(q);                                               \
          }                                                                      \
                                                                                 \

      {- -------------------------------------------
  (1.1.1) 今回見つけた dead 領域の先頭に, 新しい LiveRange オブジェクトを作って埋め込んでおく.
          (この段階では, LiveRange の終端は適当な値にしておく (とりあえず先頭と同じアドレスにしている))
          ---------------------------------------- -}

          /* record the current LiveRange object.                                \
           * liveRange->start() is overlaid on the mark word.                    \
           */                                                                    \
          liveRange = (LiveRange*)q;                                             \
          liveRange->set_start(end);                                             \
          liveRange->set_end(end);                                               \
                                                                                 \

      {- -------------------------------------------
  (1.1.1) もしこれが最初の dead オブジェクトであれば, first_dead 変数の値を変更しておく.
          ---------------------------------------- -}

          /* see if this is the first dead region. */                            \
          if (q < first_dead) {                                                  \
            first_dead = q;                                                      \
          }                                                                      \
                                                                                 \

      {- -------------------------------------------
  (1.1.1) q を次の live オブジェクト(end)に設定する
          (これにより, この後の dead オブジェクトが続いている領域については処理はスキップされる)
          ---------------------------------------- -}

          /* move on to the next object */                                       \
          q = end;                                                               \
        }                                                                        \
      }                                                                          \
                                                                                 \

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

      assert(q == t, "just checking");                                           \

  {- -------------------------------------------
  (1) 最後の LiveRange の終端を現在箇所(q)に設定する.
      ---------------------------------------- -}

      if (liveRange != NULL) {                                                   \
        liveRange->set_end(q);                                                   \
      }                                                                          \

  {- -------------------------------------------
  (1) end_of_live や first_dead の情報は phase3 や phase4 でも使うため, 
      _end_of_live フィールドおよび _first_dead フィールドに記録しておく.
      ---------------------------------------- -}

      _end_of_live = end_of_live;                                                \
      if (end_of_live < first_dead) {                                            \
        first_dead = end_of_live;                                                \
      }                                                                          \
      _first_dead = first_dead;                                                  \
                                                                                 \

  {- -------------------------------------------
  (1) CompactibleSpace::set_compaction_top() を呼んで, 
      コンパクション先の領域について, コンパクション先アドレスの情報を更新しておく. 
      ---------------------------------------- -}

      /* save the compaction_top of the compaction space. */                     \
      cp->space->set_compaction_top(compact_top);                                \
    }

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