内存代管理器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);
}


















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