內存代管理器DefNewGeneration的對象內存分配

    內存代管理器DefNewGeneration是所有基於內存分代管理的內存堆管理器的默認年青代管理器,同時它也只能用於年青代的內存管理而不能用於舊生代的內存管理.它的基本思路就是將整個內存代分配爲三個區: Eden區, From區, To區; 一般情況下,只在Eden區和From區分配對象創建時所需的內存,To區只在內存代進行垃圾對象回收用於存放轉存儲過來的非垃圾對象(active對象). 本質上, 內存代管理器是內存堆管理器中的一個組件(分治思想),它的職責也就無非一是對象內存的分配,二是垃圾對象的回收.本文將主要圍繞它的對象內存分配功能來展開,至於它內部的垃圾對象是如何回收的,筆者將在介紹完GenCollectedHeap的Gc過程之後會有詳細的闡述.


前文在介紹內存堆管理器GenCollectedHeap處理對象內存分配請求的時候涉及到幾個關於內存代管理器的核心方法,所以本文將圍繞這幾個方法來詳細的介紹默認的年輕代內存管理器DefNewGeneration分配對象內存的工作原理.


一.DefNewGeneration的創建初始化

     DefNewGeneration所管理的內存主要交由Eden/From/To三個內存區管理器來控制,而這三個內存區的大小是由參數SurvivorRatio決定的,該參數的默認值是8.一般情況下,From內存區和To內存區的大小是一樣的.具體計算方法如下:

// 根據配置參數SurvivorRatio來確定年青代中Eden/From/To三個內存區的大小
  size_t compute_survivor_size(size_t gen_size, size_t alignment) const {
    size_t n = gen_size / (SurvivorRatio + 2);
    return n > alignment ? align_size_down(n, alignment) : alignment;
  }
    在內存代管理器DefNewGeneration的創建過程中還涉及到兩個很重要的參數MaxTenuringThresholdPretenureSizeThreshold,它們的默認是分別是15和0(0在這裏表示無窮大).MaxTenuringThreshold對應的是DefNewGeneration中的_tenuring_threshold屬性,它所表示的意義是,如果年青代中的一個對象在經歷MaxTenuringThreshold次Gc之後仍然是active的,則應該將其轉存儲到舊生代.PretenureSizeThreshold對應的是DefNewGeneration的屬性_pretenure_size_threshold_words,它表示如果一個對象所需的內存空間大於該值,則不在當前年青代不予分配內存,相關算法如下:

/**
   * 當前內存代是否應該分配指定大小的內存
   * 	1).申請內存大小未溢出
   * 	2).申請內存大小不爲0
   * 	3).申請內存大小未超過本內存代的限制閾值
   */
  virtual bool should_allocate(size_t word_size, bool is_tlab) {
    assert(UseTLAB || !is_tlab, "Should not allocate tlab");

    size_t overflow_limit    = (size_t)1 << (BitsPerSize_t - LogHeapWordSize);

    const bool non_zero      = word_size > 0;
    const bool overflows     = word_size >= overflow_limit;

    //如果是爲某一對象申請空間則檢查申請的內存是否太大
    const bool check_too_big = _pretenure_size_threshold_words > 0;
    const bool not_too_big   = word_size < _pretenure_size_threshold_words;
    const bool size_ok       = is_tlab || !check_too_big || not_too_big;

    bool result = !overflows && non_zero && size_ok;

    return result;
  }

   與MaxTenuringThreshold相關的算法會在介紹DefNewGeneration執行Gc時詳細介紹, 它的創建及初始化流程如下:

DefNewGeneration::DefNewGeneration(ReservedSpace rs,
                                   size_t initial_size,
                                   int level,
                                   const char* policy)
  : Generation(rs, initial_size, level),
    _promo_failure_drain_in_progress(false),
    _should_allocate_from_space(false)
{
  MemRegion cmr((HeapWord*)_virtual_space.low(), (HeapWord*)_virtual_space.high());

  Universe::heap()->barrier_set()->resize_covered_region(cmr);

  //創建Eden內存區管理器
  if (GenCollectedHeap::heap()->collector_policy()->has_soft_ended_eden()) {
	printf("%s[%d] [tid: %lu]: 試圖創建Eden內存區管理器(ConcEdenSpace)...\n", __FILE__, __LINE__, pthread_self());

	_eden_space = new ConcEdenSpace(this);
  } else {
	printf("%s[%d] [tid: %lu]: 試圖創建Eden內存區管理器(EdenSpace)...\n", __FILE__, __LINE__, pthread_self());

    _eden_space = new EdenSpace(this);
  }


  printf("%s[%d] [tid: %lu]: 試圖創建From/To內存區管理器(ContiguousSpace)...\n", __FILE__, __LINE__, pthread_self());
  //創建From/To內存區管理器
  _from_space = new ContiguousSpace();
  _to_space   = new ContiguousSpace();

  if (_eden_space == NULL || _from_space == NULL || _to_space == NULL)
    vm_exit_during_initialization("Could not allocate a new gen space");

  // Compute the maximum eden and survivor space sizes. These sizes
  // are computed assuming the entire reserved space is committed.
  // These values are exported as performance counters.
  uintx alignment = GenCollectedHeap::heap()->collector_policy()->min_alignment();
  uintx size = _virtual_space.reserved_size();

  //計算From區和To區的最大大小
  _max_survivor_size = compute_survivor_size(size, alignment);
  //Eden區最大大小
  _max_eden_size = size - (2*_max_survivor_size);

  // allocate the performance counters

  /**
   * 創建相關的計數器
   */
  // Generation counters -- generation 0, 3 subspaces
  _gen_counters = new GenerationCounters("new", 0, 3, &_virtual_space);
  _gc_counters = new CollectorCounters(policy, 0);

  _eden_counters = new CSpaceCounters("eden", 0, _max_eden_size, _eden_space, _gen_counters);
  _from_counters = new CSpaceCounters("s0", 1, _max_survivor_size, _from_space, _gen_counters);
  _to_counters = new CSpaceCounters("s1", 2, _max_survivor_size, _to_space, _gen_counters);

  //分配對應的物理內存區
  compute_space_boundaries(0, SpaceDecorator::Clear, SpaceDecorator::Mangle);

  update_counters();

  _next_gen = NULL;

  ///對象可進入下一個內存代的存活時間閾值
  _tenuring_threshold = MaxTenuringThreshold;

  //內存代允許分配的最大對象大小
  _pretenure_size_threshold_words = PretenureSizeThreshold >> LogHeapWordSize;

  printf("%s[%d] [tid: %lu]: 默認年青代內存代管理器[%d:DefNewGeneration]{max_survivor_size=%lu, max_eden_size=%lu, pretenure_size_threshold_words(一次可申請的最大內存塊)=%lu}...\n", __FILE__, __LINE__, pthread_self(),
		  level, _max_survivor_size, _max_eden_size, _pretenure_size_threshold_words);

}
    方法compute_space_boundaries不僅在DefNewGeneration創建的時候會調用,而且在內存代Gc之後調整內存代大小時被經常調用:

/**
 * 確定三個內存區Eden/From/To在內存代中的物理地址邊界即分配對應的物理內存區
 */
void DefNewGeneration::compute_space_boundaries(uintx minimum_eden_size,
                                                bool clear_space,
                                                bool mangle_space) {
  uintx alignment = GenCollectedHeap::heap()->collector_policy()->min_alignment();

  // If the spaces are being cleared (only done at heap initialization
  // currently), the survivor spaces need not be empty.
  // Otherwise, no care is taken for used areas in the survivor spaces
  // so check.
  assert(clear_space || (to()->is_empty() && from()->is_empty()),
    "Initialization of the survivor spaces assumes these are empty");

  //根據內存代當前的初始化大小(已經申請的內存大小)來計算各內存區的大小
  uintx size = _virtual_space.committed_size(); //當前內存代已經申請到的內存大小
  uintx survivor_size = compute_survivor_size(size, alignment);
  uintx eden_size = size - (2*survivor_size);
  assert(eden_size > 0 && survivor_size <= eden_size, "just checking");

  /**
   * 如果Eden的實際大小小於配置的最小值,則調整From區和To的大小以保證Eden區的大小
   */
  if (eden_size < minimum_eden_size) {
    // May happen due to 64Kb rounding, if so adjust eden size back up
    minimum_eden_size = align_size_up(minimum_eden_size, alignment);
    uintx maximum_survivor_size = (size - minimum_eden_size) / 2;
    uintx unaligned_survivor_size = align_size_down(maximum_survivor_size, alignment);
    survivor_size = MAX2(unaligned_survivor_size, alignment);
    eden_size = size - (2*survivor_size);
    assert(eden_size > 0 && survivor_size <= eden_size, "just checking");
    assert(eden_size >= minimum_eden_size, "just checking");
  }

  /**
   * 根據各內存區的大小配置其對應的內存在內存代中的起始-結束偏移位置
   */

  char *eden_start = _virtual_space.low();
  char *from_start = eden_start + eden_size;
  char *to_start   = from_start + survivor_size;
  char *to_end     = to_start   + survivor_size;

  assert(to_end == _virtual_space.high(), "just checking");
  assert(Space::is_aligned((HeapWord*)eden_start), "checking alignment");
  assert(Space::is_aligned((HeapWord*)from_start), "checking alignment");
  assert(Space::is_aligned((HeapWord*)to_start),   "checking alignment");

  MemRegion edenMR((HeapWord*)eden_start, (HeapWord*)from_start);
  MemRegion fromMR((HeapWord*)from_start, (HeapWord*)to_start);
  MemRegion toMR  ((HeapWord*)to_start, (HeapWord*)to_end);

  // A minimum eden size implies that there is a part of eden that
  // is being used and that affects the initialization of any
  // newly formed eden.
  bool live_in_eden = minimum_eden_size > 0;

  // If not clearing the spaces, do some checking to verify that
  // the space are already mangled.
  if (!clear_space) {
    // Must check mangling before the spaces are reshaped.  Otherwise,
    // the bottom or end of one space may have moved into another
    // a failure of the check may not correctly indicate which space
    // is not properly mangled.
    if (ZapUnusedHeapArea) {
      HeapWord* limit = (HeapWord*) _virtual_space.high();
      eden()->check_mangled_unused_area(limit);
      from()->check_mangled_unused_area(limit);
        to()->check_mangled_unused_area(limit);
    }
  }

  /**
   * 初始化個內存區管理器
   */

  printf("%s[%d] [tid: %lu]: 試圖初始化Eden內存區管理器...\n", __FILE__, __LINE__, pthread_self());

  // Reset the spaces for their new regions.
  eden()->initialize(edenMR,
                     clear_space && !live_in_eden,
                     SpaceDecorator::Mangle);
  // If clear_space and live_in_eden, we will not have cleared any
  // portion of eden above its top. This can cause newly
  // expanded space not to be mangled if using ZapUnusedHeapArea.
  // We explicitly do such mangling here.
  if (ZapUnusedHeapArea && clear_space && live_in_eden && mangle_space) {
    eden()->mangle_unused_area();
  }

  printf("%s[%d] [tid: %lu]: 試圖初始化From內存區管理器...\n", __FILE__, __LINE__, pthread_self());
  from()->initialize(fromMR, clear_space, mangle_space);

  printf("%s[%d] [tid: %lu]: 試圖初始化To內存區管理器...\n", __FILE__, __LINE__, pthread_self());
  to()->initialize(toMR, clear_space, mangle_space);

  // Set next compaction spaces.
  eden()->set_next_compaction_space(from());
  // The to-space is normally empty before a compaction so need
  // not be considered.  The exception is during promotion
  // failure handling when to-space can contain live objects.
  from()->set_next_compaction_space(NULL);
}



二.DefNewGeneration的對象內存分配

1.快速分配

   這裏的快速分配是一種多線程安全的分配方式,也就是內存代管理器其內部實現就應該保證外部線程的多併發安全性,而不需要外部調用者(內存堆管理器)使用額外的鎖來自己保證多線程安全.DefNewGeneration的實現方式是:

/**
 * 從Eden區快速分配(支持併發)
 */
HeapWord* DefNewGeneration::par_allocate(size_t word_size, bool is_tlab) {
  return eden()->par_allocate(word_size);
}
  DefNewGeneration並沒有自己來實現這種策略,二是交由Eden內存區管理器來完成,以它的一種實現EdenSpace爲例(是以自旋鎖的實現方式來保證多線程安全的):

/**
 * 無鎖式併發分配
 */
HeapWord* EdenSpace::par_allocate(size_t size) {
  return par_allocate_impl(size, soft_end());
}

/**
 * 從當前的內存區中分配指定大小的一塊內存空間
 * 輪尋方式: 空閒內存足夠則嘗試分配,不夠立即結束返回
 */
inline HeapWord* ContiguousSpace::par_allocate_impl(size_t size,
                                                    HeapWord* const end_value) {
  do {
    HeapWord* obj = top();
    if (pointer_delta(end_value, obj) >= size) {	//內存區當前的空閒空間足夠分配,則嘗試分配
      HeapWord* new_top = obj + size;
      HeapWord* result = (HeapWord*)Atomic::cmpxchg_ptr(new_top, top_addr(), obj);
      // result can be one of two:
      //  the old top value: the exchange succeeded
      //  otherwise: the new value of the top is returned.
      if (result == obj) {
        assert(is_aligned(obj) && is_aligned(new_top), "checking alignment");
        return obj;
      }
    } else {
      return NULL;
    }
  } while (true);
}



2.普通分配

    普通分配約定的是一種串行分配,即不保證多線程安全,如果外部調用者存在多線程調用的情況則須自己使用全局鎖來確保多線程安全,DefNewGeneration實現這種分配方式的大致策略是:

     1).以並行的方式快速從Eden區分配內存

     2).擴展Eden區內存空間的方式分配內存
     3).從From區分配內存

具體算法如下:

/**
 * 從當前的內存代中分配指定大小的內存空間
 */
HeapWord* DefNewGeneration::allocate(size_t word_size,
                                     bool is_tlab) {
  // This is the slow-path allocation for the DefNewGeneration.
  // Most allocations are fast-path in compiled code.
  // We try to allocate from the eden.  If that works, we are happy.
  // Note that since DefNewGeneration supports lock-free allocation, we
  // have to use it here, as well.
	
  //從Eden區快速分配內存
  HeapWord* result = eden()->par_allocate(word_size);
  if (result != NULL) {
    return result;
  }

  do {
	  //擴張Eden區
    HeapWord* old_limit = eden()->soft_end();
    if (old_limit < eden()->end()) {
      //通知下一個內存代管理器,Eden區使用達到了邏輯(軟)限制
      //由下一個內存代管理器來決定Eden區新的軟限制位置
      HeapWord* new_limit = next_gen()->allocation_limit_reached(eden(), eden()->top(), word_size);
      if (new_limit != NULL) {
        Atomic::cmpxchg_ptr(new_limit, eden()->soft_end_addr(), old_limit);
      } else {
        assert(eden()->soft_end() == eden()->end(), "invalid state after allocation_limit_reached returned null");
      }
    } else {
      // The allocation failed and the soft limit is equal to the hard limit,
      // there are no reasons to do an attempt to allocate
      assert(old_limit == eden()->end(), "sanity check");
      break;
    }

    //擴張Eden區後,再次嘗試快速分配
    result = eden()->par_allocate(word_size);
  } while (result == NULL);

  // If the eden is full and the last collection bailed out, we are running
  // out of heap space, and we try to allocate the from-space, too.
  // allocate_from_space can't be inlined because that would introduce a
  // circular dependency at compile time.
  if (result == NULL) {	//Eden區沒有足夠的空間,則從From區分配
    result = allocate_from_space(word_size);
  }

  return result;
}

/**
 * 內存管理器從From區分配內存(最後一次嘗試)
 */
HeapWord* DefNewGeneration::allocate_from_space(size_t size) {
  HeapWord* result = NULL;
  if (Verbose && PrintGCDetails) {
    gclog_or_tty->print("DefNewGeneration::allocate_from_space(%u):"
                        "  will_fail: %s"
                        "  heap_lock: %s"
                        "  free: " SIZE_FORMAT,
                        size,
                        GenCollectedHeap::heap()->incremental_collection_will_fail(false /* don't consult_young */) ?
                          "true" : "false",
                        Heap_lock->is_locked() ? "locked" : "unlocked",
                        from()->free());
  }

  if (should_allocate_from_space() || GC_locker::is_active_and_needs_gc()) {
    if (Heap_lock->owned_by_self() ||
        (SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread())) {
      // If the Heap_lock is not locked by this thread, this will be called
      // again later with the Heap_lock held.
      result = from()->allocate(size);
    } else if (PrintGC && Verbose) {
      gclog_or_tty->print_cr("  Heap_lock is not owned by self");
    }
  } else if (PrintGC && Verbose) {
    gclog_or_tty->print_cr("  should_allocate_from_space: NOT");
  }

  if (PrintGC && Verbose) {
    gclog_or_tty->print_cr("  returns %s", result == NULL ? "NULL" : "object");
  }

  return result;
}

  From內存區管理器(ContiguousSpace)分配內存空間的算法如下:

/**
 * 加鎖式分配,由調用線程加外部鎖來保證多線程併發安全
 */
HeapWord* ContiguousSpace::allocate(size_t size) {
  return allocate_impl(size, end());
}

/**
 * 內存區管理器有瑣式分配
 */
inline HeapWord* ContiguousSpace::allocate_impl(size_t size,
                                                HeapWord* const end_value) {
  // In G1 there are places where a GC worker can allocates into a
  // region using this serial allocation code without being prone to a
  // race with other GC workers (we ensure that no other GC worker can
  // access the same region at the same time). So the assert below is
  // too strong in the case of G1.
  assert(Heap_lock->owned_by_self() ||
         (SafepointSynchronize::is_at_safepoint() &&
                               (Thread::current()->is_VM_thread() || UseG1GC)),
         "not locked");
  HeapWord* obj = top();
  if (pointer_delta(end_value, obj) >= size) {	//當前內存區內存足夠,則分配
    HeapWord* new_top = obj + size;
    set_top(new_top);
    assert(is_aligned(obj) && is_aligned(new_top), "checking alignment");
    return obj;
  } else {
    return NULL;
  }
}

3.擴展內存代空間再分配

   內存代管理器DefNewGeneration並不支持這種約定的分配方式,而是依賴於普通分配的方式在其內部實現:

/**
 * 默認的年青代內存管理器並不支持擴展內存再分配方式
 */
HeapWord* DefNewGeneration::expand_and_allocate(size_t size,
                                                bool   is_tlab,
                                                bool   parallel) {
  // We don't attempt to expand the young generation (but perhaps we should.)
  return allocate(size, is_tlab);
}


















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