內存代管理器TenuredGeneration對垃圾對象的回收

   內存代管理器TenuredGeneration作爲基於內存分代管理的內存堆管理器GenCollectedHeap默認的舊生代管理器,它對垃圾對象的回收算法要比年青代Gc要複雜的多,但其主體思路就是:標記-清除-壓縮.本文主要圍繞內存代Gc時的三個過程來詳細講解TenuredGeneration是如何進行垃圾回收的.

一.Gc條件

     可對 TenuredGeneration管理的舊生代進行Gc的條件主要有4個,滿足其中的任何一個即可對舊生代進行垃圾回收:

1.當前是Full Gc
2.可在當前內存代分配請求的空間
3.當前內存代空閒空間<10000
4.當前內存代容量>上一次Gc之前的容量
     具體代碼可參見如下:

bool TenuredGeneration::should_collect(bool  full, size_t size, bool   is_tlab) {
  // This should be one big conditional or (||), but I want to be able to tell
  // why it returns what it returns (without re-evaluating the conditionals
  // in case they aren't idempotent), so I'm doing it this way.
  // DeMorgan says it's okay.
  bool result = false;

  if (!result && full) {
    result = true;
    if (PrintGC && Verbose) {
      gclog_or_tty->print_cr("TenuredGeneration::should_collect: because full");
    }
  }

  if (!result && should_allocate(size, is_tlab)) {
    result = true;
    if (PrintGC && Verbose) {
      gclog_or_tty->print_cr("TenuredGeneration::should_collect: because"
                    " should_allocate(" SIZE_FORMAT ")", size);
    }
  }

  // If we don't have very much free space.
  // XXX: 10000 should be a percentage of the capacity!!!
  if (!result && free() < 10000) {
    result = true;
    if (PrintGC && Verbose) {
      gclog_or_tty->print_cr("TenuredGeneration::should_collect: because"
                    " free(): " SIZE_FORMAT,
                    free());
    }
  }

  // If we had to expand to accomodate promotions from younger generations
  if (!result && _capacity_at_prologue < capacity()) {
    result = true;
    if (PrintGC && Verbose) {
      gclog_or_tty->print_cr("TenuredGeneration::should_collect: because"
                    "_capacity_at_prologue: " SIZE_FORMAT " < capacity(): " SIZE_FORMAT,
                    _capacity_at_prologue, capacity());
    }
  }

  return result;
}

二.Gc基本流程

       默認的舊生代管理器TenuredGeneration回收垃圾對象的基本思路就是:

第一步: 標記所有的active對象

第二步: 計算所有active對象在其內存代壓縮後的偏移位置

第三步: 更新所有active對象的地址映射表

第四步: 移動複製所有的active對象到新的存儲位置

1.標記所有的active對象

   TenuredGeneration標記所有active對象的過程跟年青代Gc相似,都是從根對象開始以深度優先的方式搜索標記所有的active對象,具體過程如下:

/**
 * 從根對象開始遞歸迭代標記所有活動的對象
 */
void GenMarkSweep::mark_sweep_phase1(int level,
                                  bool clear_all_softrefs) {
  // Recursively traverse all live objects and mark them
  TraceTime tm("phase 1", PrintGC && Verbose, true, gclog_or_tty);
  trace(" 1");

  VALIDATE_MARK_SWEEP_ONLY(reset_live_oop_tracking(false));

  GenCollectedHeap* gch = GenCollectedHeap::heap();

  // Because follow_root_closure is created statically, cannot
  // use OopsInGenClosure constructor which takes a generation,
  // as the Universe has not been created when the static constructors
  // are run.
  follow_root_closure.set_orig_generation(gch->get_gen(level));

  /**
   * 遍歷當前所有的根對象並標記,並遞歸遍歷標記它們的引用對象
   */
  gch->gen_process_strong_roots(level,
                                false, // Younger gens are not roots.
                                true,  // activate StrongRootsScope
                                true,  // Collecting permanent generation.
                                SharedHeap::SO_SystemClasses,
                                &follow_root_closure,
                                true,   // walk code active on stacks
                                &follow_root_closure);

  //標記所有的軟引用對象
  {
    ref_processor()->setup_policy(clear_all_softrefs);
    ref_processor()->process_discovered_references(&is_alive, &keep_alive, &follow_stack_closure, NULL);
  }

  // Follow system dictionary roots and unload classes
  bool purged_class = SystemDictionary::do_unloading(&is_alive);

  // 清理代碼高速緩衝區
  CodeCache::do_unloading(&is_alive, &keep_alive, purged_class);
  follow_stack(); // Flush marking stack

  // Update subklass/sibling/implementor links of live klasses
  follow_weak_klass_links();
  assert(_marking_stack.is_empty(), "just drained");

  //清理未被標記的軟/弱引用對象
  follow_mdo_weak_refs();
  assert(_marking_stack.is_empty(), "just drained");

  //清除沒有被引用的常量字符串
  StringTable::unlink(&is_alive);

  //清除符號表中沒有被引用的符號
  SymbolTable::unlink();

  assert(_marking_stack.is_empty(), "stack should be empty by now");
}

2.計算所有active對象在其內存代壓縮後的偏移位置

    以內存代爲單位計算各內存代中的active對象壓縮後的新存儲位置:

/**
 * 通過對象的標記,計算存活的對象在其內存區壓縮後的偏移位置
 */
void GenMarkSweep::mark_sweep_phase2() {
  // Now all live objects are marked, compute the new object addresses.

  // It is imperative that we traverse perm_gen LAST. If dead space is
  // allowed a range of dead object may get overwritten by a dead int
  // array. If perm_gen is not traversed last a klassOop may get
  // overwritten. This is fine since it is dead, but if the class has dead
  // instances we have to skip them, and in order to find their size we
  // need the klassOop!
  //
  // It is not required that we traverse spaces in the same order in
  // phase2, phase3 and phase4, but the ValidateMarkSweep live oops
  // tracking expects us to do so. See comment under phase4.

  GenCollectedHeap* gch = GenCollectedHeap::heap();
  Generation* pg = gch->perm_gen();

  TraceTime tm("phase 2", PrintGC && Verbose, true, gclog_or_tty);
  trace("2");

  VALIDATE_MARK_SWEEP_ONLY(reset_live_oop_tracking(false));

  //計算年青代+老生代中active對象在其內存代壓縮後的偏移位置
  gch->prepare_for_compaction();

  VALIDATE_MARK_SWEEP_ONLY(_live_oops_index_at_perm = _live_oops_index);

  //計算永久代中active對象在其內存代壓縮後的偏移位置
  CompactPoint perm_cp(pg, NULL, NULL);
  pg->prepare_for_compaction(&perm_cp);

}
   在內存代內部,又是以內存區爲單位來計算的:

/**
 * 計算內存代放入各個內存區中活着的對象在其壓縮後的偏移位置
 */
void Generation::prepare_for_compaction(CompactPoint* cp) {
  // Generic implementation, can be specialized
  CompactibleSpace* space = first_compaction_space();
  while (space != NULL) {
    space->prepare_for_compaction(cp);
    space = space->next_compaction_space();
  }
}
    內存區的計算方法是(對象新的存儲地址存放在對象實例的_makr屬性中):

/**
 * 計算內存區中所有活着(被標記)的對象在其被壓縮後的偏移位置
 */
void CompactibleSpace::prepare_for_compaction(CompactPoint* cp) {
  SCAN_AND_FORWARD(cp, end, block_is_obj, block_size);
}

#define SCAN_AND_FORWARD(cp,scan_limit,block_is_obj,block_size) {            \
  /* 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. */ \
                                                                             \
  /* 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());                                              \
                                                                             \
  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();                               \
  }                                                                          \
                                                                             \
  /* 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 = (MarkSweepAlwaysCompactCount < 1)                         \
    ||((invocations % MarkSweepAlwaysCompactCount) != 0);                    \
                                                                             \
  size_t allowed_deadspace = 0;                                              \
  if (skip_dead) {                                                           \
    const size_t ratio = allowed_dead_ratio();                               \
    allowed_deadspace = (capacity() * ratio / 100) / HeapWordSize;           \
  }                                                                          \
                                                                             \
  /**                                                                        \
   * 當前內存區壓縮的開始位置和結束位置                                                                                                                                                   \
   */                                                                        \
  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. */   \
  _first_dead = first_dead;   /*當前內存區中最後一個active對象*/                 \
                                                                             \
  const intx interval = PrefetchScanIntervalInBytes;                         \
                                                                             \
   /**                                                                       \
    * 開始遍歷該內存區分配的所有對象                                                                                                                                                        \
    */         																 \
  while (q < t) {                                                            \
    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");           \
    /**                                                                      \
     * active對象,則確定其新的存儲位置                                           \
     */                                                                      \
    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;                                                       \
    } else {                                                                 \
      /* 垃圾對象,則一直遍歷到其下一個active對象 */                                \
      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()));\
                                                                             \
      /* 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. */                           \
                                                                             \
      /* for the previous LiveRange, record the end of the live objects. */  \
      if (liveRange) {                                                       \
        liveRange->set_end(q);                                               \
      }                                                                      \
                                                                             \
      /* record the current LiveRange object.                                \
       * liveRange->start() is overlaid on the mark word.                    \
       */                                                                    \
      liveRange = (LiveRange*)q;                                             \
      liveRange->set_start(end);                                             \
      liveRange->set_end(end);                                               \
                                                                             \
      /* see if this is the first dead region. */                            \
      if (q < first_dead) {                                                  \
        first_dead = q;                                                      \
      }                                                                      \
                                                                             \
      /* move on to the next object */                                       \
      q = end;                                                               \
    }                                                                        \
  }                                                                          \
                                                                             \
  assert(q == t, "just checking");                                           \
  if (liveRange != NULL) {                                                   \
    liveRange->set_end(q);                                                   \
  }                                                                          \
  _end_of_live = end_of_live;                                                \
  if (end_of_live < first_dead) {                                            \
    first_dead = end_of_live;                                                \
  }                                                                          \
  _first_dead = first_dead;                                                  \
                                                                             \
  /* save the compaction_top of the compaction space. */                     \
  cp->space->set_compaction_top(compact_top);                                \
}

3.更新所有active對象的地址映射表

   該過程的實現和標記所有的active對象很相似,就是一個是標記,一個是更新地址指針:

/**
 * 從根對象開始遞歸迭代更新所有活動對象的地址映射表(調整所有的對象指針)
 */
void GenMarkSweep::mark_sweep_phase3(int level) {
  GenCollectedHeap* gch = GenCollectedHeap::heap();
  Generation* pg = gch->perm_gen();

  // Adjust the pointers to reflect the new locations
  TraceTime tm("phase 3", PrintGC && Verbose, true, gclog_or_tty);
  trace("3");

  VALIDATE_MARK_SWEEP_ONLY(reset_live_oop_tracking(false));

  // Needs to be done before the system dictionary is adjusted.
  pg->pre_adjust_pointers();

  // Because the two closures below are created statically, cannot
  // use OopsInGenClosure constructor which takes a generation,
  // as the Universe has not been created when the static constructors
  // are run.
  adjust_root_pointer_closure.set_orig_generation(gch->get_gen(level));
  adjust_pointer_closure.set_orig_generation(gch->get_gen(level));

  gch->gen_process_strong_roots(level,
                                false, // Younger gens are not roots.
                                true,  // activate StrongRootsScope
                                true,  // Collecting permanent generation.
                                SharedHeap::SO_AllClasses,
                                &adjust_root_pointer_closure,
                                false, // do not walk code
                                &adjust_root_pointer_closure);

  // Now adjust pointers in remaining weak roots.  (All of which should
  // have been cleared if they pointed to non-surviving objects.)
  CodeBlobToOopClosure adjust_code_pointer_closure(&adjust_pointer_closure,
                                                   /*do_marking=*/ false);
  gch->gen_process_weak_roots(&adjust_root_pointer_closure,
                              &adjust_code_pointer_closure,
                              &adjust_pointer_closure);

  adjust_marks();
  GenAdjustPointersClosure blk;
  gch->generation_iterate(&blk, true);
  pg->adjust_pointers();
}
   這裏的對象地址映射表實際上就是前面的博文內存堆Gc時公認的根對象 中提到的實例對象句柄.

/**
 * 調整對象的物理地址指針
 */
template <class T> inline void MarkSweep::adjust_pointer(T* p, bool isroot) {
  T heap_oop = oopDesc::load_heap_oop(p);

  if (!oopDesc::is_null(heap_oop)) {
    oop obj     = oopDesc::decode_heap_oop_not_null(heap_oop);
    oop new_obj = oop(obj->mark()->decode_pointer());	//對象新的地址指針
    assert(new_obj != NULL ||                         // is forwarding ptr?
           obj->mark() == markOopDesc::prototype() || // not gc marked?
           (UseBiasedLocking && obj->mark()->has_bias_pattern()) ||
                                                      // not gc marked?
           obj->is_shared(),                          // never forwarded?
           "should be forwarded");

    if (new_obj != NULL) {
      assert(Universe::heap()->is_in_reserved(new_obj),
             "should be in object space");

      //更新對象的物理地址指針
      oopDesc::encode_store_heap_oop_not_null(p, new_obj);
    }
  }

  VALIDATE_MARK_SWEEP_ONLY(track_adjusted_pointer(p, isroot));
}

4.移動複製所有的active對象到新的存儲位置

    該過程跟計算所有active對象在其內存代壓縮後的偏移位置的操作流程很相似,這裏不在詳細贅述.

三.Gc後的內存代大小調整

       內存堆管理器在對某些內存代進行Gc之後,基本都會調整他們的內存容量,即對內存代進行擴容還是縮容.這個操作主要受控於兩個參數:最小空閒率(MinHeapFreeRatio)和最大空閒率MaxHeapFreeRatio.TenuredGeneration根據這兩個參數來調整舊生代的容量過程如下:

/**
 * 調整當前內存代的容量(一般發生在一次Gc之後)
 */
void TenuredGeneration::compute_new_size() {
  assert(_shrink_factor <= 100, "invalid shrink factor");
  size_t current_shrink_factor = _shrink_factor;
  _shrink_factor = 0;

  printf("%s[%d] [tid: %lu]: 當前內存代[%s]開始調整其容量大小.\n", __FILE__, __LINE__, pthread_self(), this->name());

  // We don't have floating point command-line arguments
  // Note:  argument processing ensures that MinHeapFreeRatio < 100.
  const double minimum_free_percentage = MinHeapFreeRatio / 100.0;	//內存代的最小空閒率
  const double maximum_used_percentage = 1.0 - minimum_free_percentage;

  //一次Gc之後,當前內存代的使用量和容量
  const size_t used_after_gc = used();
  const size_t capacity_after_gc = capacity();

  //計算當前內存代期望的最小容量
  const double min_tmp = used_after_gc / maximum_used_percentage;
  size_t minimum_desired_capacity = (size_t)MIN2(min_tmp, double(max_uintx));
  // Don't shrink less than the initial generation size
  minimum_desired_capacity = MAX2(minimum_desired_capacity, spec()->init_size());

  assert(used_after_gc <= minimum_desired_capacity, "sanity check");

  if (PrintGC && Verbose) {
    const size_t free_after_gc = free();
    const double free_percentage = ((double)free_after_gc) / capacity_after_gc;
    gclog_or_tty->print_cr("TenuredGeneration::compute_new_size: ");
    gclog_or_tty->print_cr("  "
                  "  minimum_free_percentage: %6.2f"
                  "  maximum_used_percentage: %6.2f",
                  minimum_free_percentage,
                  maximum_used_percentage);
    gclog_or_tty->print_cr("  "
                  "   free_after_gc   : %6.1fK"
                  "   used_after_gc   : %6.1fK"
                  "   capacity_after_gc   : %6.1fK",
                  free_after_gc / (double) K,
                  used_after_gc / (double) K,
                  capacity_after_gc / (double) K);
    gclog_or_tty->print_cr("  "
                  "   free_percentage: %6.2f",
                  free_percentage);
  }

  //當前內存代的實際容量小於期望的容量,則擴展當前內存代的容量
  if (capacity_after_gc < minimum_desired_capacity) {
    // If we have less free space than we want then expand
    size_t expand_bytes = minimum_desired_capacity - capacity_after_gc;

    // Don't expand unless it's significant
    if (expand_bytes >= _min_heap_delta_bytes) {
      printf("%s[%d] [tid: %lu]: Gc之後,試圖爲內存代[%s]的容量擴大 %lu bytes.\n", __FILE__, __LINE__, pthread_self(), this->name(), expand_bytes);
      expand(expand_bytes, 0); // safe if expansion fails
    }

    if (PrintGC && Verbose) {
      gclog_or_tty->print_cr("    expanding:"
                    "  minimum_desired_capacity: %6.1fK"
                    "  expand_bytes: %6.1fK"
                    "  _min_heap_delta_bytes: %6.1fK",
                    minimum_desired_capacity / (double) K,
                    expand_bytes / (double) K,
                    _min_heap_delta_bytes / (double) K);
    }

    return;
  }

  // No expansion, now see if we want to shrink
  size_t shrink_bytes = 0;
  // We would never want to shrink more than this
  size_t max_shrink_bytes = capacity_after_gc - minimum_desired_capacity;

  if (MaxHeapFreeRatio < 100) {
	//計算當前內存代期望的大容量
    const double maximum_free_percentage = MaxHeapFreeRatio / 100.0;
    const double minimum_used_percentage = 1.0 - maximum_free_percentage;
    const double max_tmp = used_after_gc / minimum_used_percentage;
    size_t maximum_desired_capacity = (size_t)MIN2(max_tmp, double(max_uintx));
    maximum_desired_capacity = MAX2(maximum_desired_capacity, spec()->init_size());

    if (PrintGC && Verbose) {
      gclog_or_tty->print_cr("  "
                             "  maximum_free_percentage: %6.2f"
                             "  minimum_used_percentage: %6.2f",
                             maximum_free_percentage,
                             minimum_used_percentage);
      gclog_or_tty->print_cr("  "
                             "  _capacity_at_prologue: %6.1fK"
                             "  minimum_desired_capacity: %6.1fK"
                             "  maximum_desired_capacity: %6.1fK",
                             _capacity_at_prologue / (double) K,
                             minimum_desired_capacity / (double) K,
                             maximum_desired_capacity / (double) K);
    }
    assert(minimum_desired_capacity <= maximum_desired_capacity,
           "sanity check");

    //當前內存代Gc之後的容量大於期望的最大容量
    if (capacity_after_gc > maximum_desired_capacity) {
      // Capacity too large, compute shrinking size
      shrink_bytes = capacity_after_gc - maximum_desired_capacity;
      // We don't want shrink all the way back to initSize if people call
      // System.gc(), because some programs do that between "phases" and then
      // we'd just have to grow the heap up again for the next phase.  So we
      // damp the shrinking: 0% on the first call, 10% on the second call, 40%
      // on the third call, and 100% by the fourth call.  But if we recompute
      // size without shrinking, it goes back to 0%.
      shrink_bytes = shrink_bytes / 100 * current_shrink_factor;
      assert(shrink_bytes <= max_shrink_bytes, "invalid shrink size");

      if (current_shrink_factor == 0) {
        _shrink_factor = 10;
      } else {
        _shrink_factor = MIN2(current_shrink_factor * 4, (size_t) 100);
      }

      if (PrintGC && Verbose) {
        gclog_or_tty->print_cr("  "
                      "  shrinking:"
                      "  initSize: %.1fK"
                      "  maximum_desired_capacity: %.1fK",
                      spec()->init_size() / (double) K,
                      maximum_desired_capacity / (double) K);
        gclog_or_tty->print_cr("  "
                      "  shrink_bytes: %.1fK"
                      "  current_shrink_factor: %d"
                      "  new shrink factor: %d"
                      "  _min_heap_delta_bytes: %.1fK",
                      shrink_bytes / (double) K,
                      current_shrink_factor,
                      _shrink_factor,
                      _min_heap_delta_bytes / (double) K);
      }
    }
  }

  if (capacity_after_gc > _capacity_at_prologue) {
    //當前內存代Gc之後的容量大於Gc之前的容量,那麼就是在內存代Gc時爲了存儲升級來的active對象而擴展了內存容量,
	//現在至少應該縮小到Gc之前的容量大小
    size_t expansion_for_promotion = capacity_after_gc - _capacity_at_prologue;
    expansion_for_promotion = MIN2(expansion_for_promotion, max_shrink_bytes);
    // We have two shrinking computations, take the largest
    shrink_bytes = MAX2(shrink_bytes, expansion_for_promotion);

    assert(shrink_bytes <= max_shrink_bytes, "invalid shrink size");
    if (PrintGC && Verbose) {
      gclog_or_tty->print_cr("  "
                             "  aggressive shrinking:"
                             "  _capacity_at_prologue: %.1fK"
                             "  capacity_after_gc: %.1fK"
                             "  expansion_for_promotion: %.1fK"
                             "  shrink_bytes: %.1fK",
                             capacity_after_gc / (double) K,
                             _capacity_at_prologue / (double) K,
                             expansion_for_promotion / (double) K,
                             shrink_bytes / (double) K);
    }
  }

  // Don't shrink unless it's significant
  if (shrink_bytes >= _min_heap_delta_bytes) {
	printf("%s[%d] [tid: %lu]: Gc之後,試圖爲內存代[%s]的容量縮小 %lu bytes.\n", __FILE__, __LINE__, pthread_self(), this->name(), shrink_bytes);
    shrink(shrink_bytes);
  }

  assert(used() == used_after_gc && used_after_gc <= capacity(),
         "sanity check");
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章