内存代管理器TenuredGeneration的对象内存分配

  内存代管理器TenuredGeneration是基于内存分代管理的内存堆管理器GenCollectedHeap默认的旧生代管理器,对于java对象的内存分配处理还是比较简单的,但在垃圾对象回收方面.它的实现可能要远比新生代管理器要复杂的多.前文在介绍GenCollectedHeap是如何响应对象内存申请时就涉及到了旧生代内存管理器的几个核心的抽象接口,所以本文将主要介绍TenuredGeneration对这些方法的实现以及相关的组件.



一.TenuredGeneration的创建初始化

      在旧生代内存管理器TenuredGeneration内部有两个核心的组件: 一是核心内存区管理器(TenuredSpace);二是内存块偏移表(BlockOffsetSharedArray). 核心内存区管理器与对象内存分配相关,内存块偏移表和Gc相关.

1.内存代管理器(TenuredGeneration)的创建与初始化

TenuredGeneration::TenuredGeneration(ReservedSpace rs,
                                     size_t initial_byte_size, int level,
                                     GenRemSet* remset) :
  OneContigSpaceCardGeneration(rs, initial_byte_size,
                               MinHeapDeltaBytes, level, remset, NULL)
{
  HeapWord* bottom = (HeapWord*) _virtual_space.low();
  HeapWord* end    = (HeapWord*) _virtual_space.high();

  printf("%s[%d] [tid: %lu]: 试图创建核心的内存区管理器..\n", __FILE__, __LINE__, pthread_self());

  //创建核心内存区管理器
  _the_space  = new TenuredSpace(_bts, MemRegion(bottom, end));
  _the_space->reset_saved_mark();

  _shrink_factor = 0;
  _capacity_at_prologue = 0;

  _gc_stats = new GCStats();

  // initialize performance counters

  const char* gen_name = "old";

  //内存代/核心内存区/Gc相关计数器
  _gen_counters = new GenerationCounters(gen_name, 1, 1, &_virtual_space);

  _gc_counters = new CollectorCounters("MSC", 1);

  _space_counters = new CSpaceCounters(gen_name, 0,
                                       _virtual_space.reserved_size(),
                                       _the_space, _gen_counters);
}

OneContigSpaceCardGeneration::OneContigSpaceCardGeneration(ReservedSpace rs, size_t initial_byte_size,
                               size_t min_heap_delta_bytes,
                               int level, GenRemSet* remset,
                               ContiguousSpace* space) :
    CardGeneration(rs, initial_byte_size, level, remset),
    _the_space(space), _min_heap_delta_bytes(min_heap_delta_bytes)
  {}

CardGeneration::CardGeneration(ReservedSpace rs, size_t initial_byte_size,
                               int level,
                               GenRemSet* remset) :
  Generation(rs, initial_byte_size, level), _rs(remset){

  HeapWord* start = (HeapWord*)rs.base();
  size_t reserved_byte_size = rs.size();

  assert((uintptr_t(start) & 3) == 0, "bad alignment");
  assert((reserved_byte_size & 3) == 0, "bad alignment");

  MemRegion reserved_mr(start, heap_word_size(reserved_byte_size));
  //创建内存块偏移表
  _bts = new BlockOffsetSharedArray(reserved_mr,
                                    heap_word_size(initial_byte_size));

  MemRegion committed_mr(start, heap_word_size(initial_byte_size));
  _rs->resize_covered_region(committed_mr);

  if (_bts == NULL)
    vm_exit_during_initialization("Could not allocate a BlockOffsetArray");

  // Verify that the start and end of this generation is the start of a card.
  // If this wasn't true, a single card could span more than on generation,
  // which would cause problems when we commit/uncommit memory, and when we
  // clear and dirty cards.
  guarantee(_rs->is_aligned(reserved_mr.start()), "generation must be card aligned");
  if (reserved_mr.end() != Universe::heap()->reserved_region().end()) {
    // Don't check at the very end of the heap as we'll assert that we're probing off
    // the end if we try.
    guarantee(_rs->is_aligned(reserved_mr.end()), "generation must be card aligned");
  }

}

Generation::Generation(ReservedSpace rs, size_t initial_size, int level) :
  _level(level),
  _ref_processor(NULL) {

  //初始化虚拟内存管理器
  if (!_virtual_space.initialize(rs, initial_size)) {
    vm_exit_during_initialization("Could not reserve enough space for "
                    "object heap");
  }

  // Mangle all of the the initial generation.
  if (ZapUnusedHeapArea) {
    MemRegion mangle_region((HeapWord*)_virtual_space.low(),
      (HeapWord*)_virtual_space.high());
    SpaceMangler::mangle_region(mangle_region);
  }

  _reserved = MemRegion((HeapWord*)_virtual_space.low_boundary(),
          (HeapWord*)_virtual_space.high_boundary());
}

2.内存块偏移表(BlockOffsetSharedArray)的创建与初始化

BlockOffsetSharedArray::BlockOffsetSharedArray(MemRegion reserved,
                                               size_t init_word_size):
  _reserved(reserved), _end(NULL)
{
  //计算内存块偏移表的大小
  size_t size = compute_size(reserved.word_size());

  printf("%s[%d] [tid: %lu]: 试图向操作系统申请大小为%lu bytes的内存空间,并映射到指定的内存起始位置...\n", __FILE__, __LINE__, pthread_self(), size);

  ReservedSpace rs(size);
  if (!rs.is_reserved()) {
    vm_exit_during_initialization("Could not reserve enough space for heap offset array");
  }
  //用申请到的内存空间来初始化虚拟内存管理器
  if (!_vs.initialize(rs, 0)) {
    vm_exit_during_initialization("Could not reserve enough space for heap offset array");
  }

  _offset_array = (u_char*)_vs.low_boundary();	//byte[]偏移表
  resize(init_word_size);

  if (TraceBlockOffsetTable) {
    gclog_or_tty->print_cr("BlockOffsetSharedArray::BlockOffsetSharedArray: ");
    gclog_or_tty->print_cr("  "
                  "  rs.base(): " INTPTR_FORMAT
                  "  rs.size(): " INTPTR_FORMAT
                  "  rs end(): " INTPTR_FORMAT,
                  rs.base(), rs.size(), rs.base() + rs.size());
    gclog_or_tty->print_cr("  "
                  "  _vs.low_boundary(): " INTPTR_FORMAT
                  "  _vs.high_boundary(): " INTPTR_FORMAT,
                  _vs.low_boundary(),
                  _vs.high_boundary());
  }
}

/**
 * 调整内存块偏移表的大小
 */
void BlockOffsetSharedArray::resize(size_t new_word_size) {
  assert(new_word_size <= _reserved.word_size(), "Resize larger than reserved");

  size_t new_size = compute_size(new_word_size);
  size_t old_size = _vs.committed_size();	//
  size_t delta;
  char* high = _vs.high();
  _end = _reserved.start() + new_word_size;
  if (new_size > old_size) {	//扩张内存块偏移表
    delta = ReservedSpace::page_align_size_up(new_size - old_size);
    assert(delta > 0, "just checking");
    if (!_vs.expand_by(delta)) {
      // Do better than this for Merlin
      vm_exit_out_of_memory(delta, "offset table expansion");
    }
    assert(_vs.high() == high + delta, "invalid expansion");
  } else {	//缩小内存块偏移表
    delta = ReservedSpace::page_align_size_down(old_size - new_size);
    if (delta == 0) return;
    _vs.shrink_by(delta);
    assert(_vs.high() == high - delta, "invalid expansion");
  }
}   

    值得注意的是,内存块偏移表所占用的内存并不会从JVM内存堆中获取的,二是直接向操作系统申请,而内存块偏移表的大小主要取决于对应内存区的大小及内存块的大小,其计算方法如下:

/**
 * 根据内存区的大小计算对应的内存块偏移表的大小
 */
size_t compute_size(size_t mem_region_words) {
  size_t number_of_slots = (mem_region_words / N_words) + 1;
  return ReservedSpace::allocation_align_size_up(number_of_slots);
}
内存块的大小 N_words是以JVM中的字(HeapWord)为单位来衡量的,在32位系统中,HeapWord为4个字节,64位系统中HeapWord为8个字节:

#ifdef _LP64
const int LogHeapWordSize     = 3;
#else
const int LogHeapWordSize     = 2;
#endif

enum SomePrivateConstants {
    LogN = 9,
    LogN_words = LogN - LogHeapWordSize,
    N_bytes = 1 << LogN,
    N_words = 1 << LogN_words
};

3.核心内存区管理器(TenuredSpace)的创建与初始化

    由于旧生代内存管理器TenuredGeneration对对象内存申请的处理都是直接交给核心内存区管理器(TenuredSpace)来处理,所以在内存分配时对内存块偏移表的操作也是由其代为完成的.

TenuredSpace::TenuredSpace(BlockOffsetSharedArray* sharedOffsetArray,
               MemRegion mr) :
    OffsetTableContigSpace(sharedOffsetArray, mr) {}

OffsetTableContigSpace::OffsetTableContigSpace(BlockOffsetSharedArray* sharedOffsetArray,
                                               MemRegion mr) :
  _offsets(sharedOffsetArray, mr),
  _par_alloc_lock(Mutex::leaf, "OffsetTableContigSpace par alloc lock", true)
{
  _offsets.set_contig_space(this);
  initialize(mr, SpaceDecorator::Clear, SpaceDecorator::Mangle);
}

void ContiguousSpace::initialize(MemRegion mr,
                                 bool clear_space,
                                 bool mangle_space)
{
  CompactibleSpace::initialize(mr, clear_space, mangle_space);
  set_concurrent_iteration_safe_limit(top());
}

void CompactibleSpace::initialize(MemRegion mr,
                                  bool clear_space,
                                  bool mangle_space) {
  Space::initialize(mr, clear_space, mangle_space);
  set_compaction_top(bottom());
  _next_compaction_space = NULL;
}

void Space::initialize(MemRegion mr,
                       bool clear_space,
                       bool mangle_space) {
  HeapWord* bottom = mr.start();
  HeapWord* end    = mr.end();
  assert(Universe::on_page_boundary(bottom) && Universe::on_page_boundary(end),
         "invalid space boundaries");

  set_bottom(bottom);
  set_end(end);

  if (clear_space) clear(mangle_space);
}

二.TenuredGeneration的对象内存分配

1.快速分配

     TenuredGeneration对无锁式分配内存的实现很扯淡, 目前并不支持这种分配方式.说它扯淡是因为:首先它不支持对给线程本地分配缓冲区分配内存块,然后核心内存区管理器(TenuredSpace)在分配内存时又加锁了(主要为了保证对内存块偏移表操作的时序性,必须串行化), 然后在OffsetTableContigSpace::par_allocate的注释上来一句"Therefore, best if this is used for larger LAB allocations only"

/**
 * 并发式无锁分配
 */
HeapWord* OneContigSpaceCardGeneration::par_allocate(size_t word_size,
                                                     bool is_tlab) {
  assert(!is_tlab, "OneContigSpaceCardGeneration does not support TLAB allocation");
  return the_space()->par_allocate(word_size);
}

inline HeapWord* OffsetTableContigSpace::par_allocate(size_t size) {
  MutexLocker x(&_par_alloc_lock);
 
  HeapWord* res = ContiguousSpace::par_allocate(size);
  //在内存块偏移表中标记该内存块
  if (res != NULL) {
    _offsets.alloc_block(res, size);
  }
  return res;
}

/**
 * 无锁式分配,由当前内存区管理器内部保证多线程并发安全
 */
HeapWord* ContiguousSpace::par_allocate(size_t size) {
  return par_allocate_impl(size, 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.普通分配

inline HeapWord* OffsetTableContigSpace::allocate(size_t size) {
  HeapWord* res = ContiguousSpace::allocate(size);
  
  //在内存块偏移表中标记该内存块
  if (res != NULL) {
    _offsets.alloc_block(res, size);
  }
  return res;
}

/**
 * 加锁式分配,由调用线程加外部锁来保证多线程并发安全
 */
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.扩展内存代空间再分配

    内存代管理器TenuredGeneration实现扩展内存代空间再分配的主要策略是:

1.串行方式:
       1).扩张当前内存代的容量
       2).串行分配指定大小的内存块
2.并行方式:
       1).扩展当前内存代的容量
       2).等待一定时间
       3).并行分配指定大小的内存块

   另外为了防止频繁扩展内存代空间,每次扩展内存代时都有一个最小值_min_heap_delta_bytes,由JVM参数MinHeapDeltaBytes决定,其默认值为128KB

/**
 * 以允许扩展内存代的物理空间的方式串行/并行分配内存块
 */
HeapWord* OneContigSpaceCardGeneration::expand_and_allocate(size_t word_size,
                                                  bool is_tlab,
                                                  bool parallel) {
  assert(!is_tlab, "OneContigSpaceCardGeneration does not support TLAB allocation");

  if (parallel) {
    MutexLocker x(ParGCRareEvent_lock);
    HeapWord* result = NULL;
    size_t byte_size = word_size * HeapWordSize;
    while (true) {
      expand(byte_size, _min_heap_delta_bytes);	//扩展当前内存代

      if (GCExpandToAllocateDelayMillis > 0) {
        os::sleep(Thread::current(), GCExpandToAllocateDelayMillis, false);
      }

      result = _the_space->par_allocate(word_size);
      if ( result != NULL) {
        return result;
      } else {
        //即使扩张内存页不够,所以放弃重试
        if (_virtual_space.uncommitted_size() < byte_size) {
          return NULL;
        }
        // else try again
      }
    }
  } else {
    expand(word_size*HeapWordSize, _min_heap_delta_bytes);	//扩展当前内存代
    return _the_space->allocate(word_size);
  }
}

bool OneContigSpaceCardGeneration::expand(size_t bytes, size_t expand_bytes) {
  GCMutexLocker x(ExpandHeap_lock);

  printf("%s[%d] [tid: %lu]: 试图扩展内存代[%s]的容量\n", __FILE__, __LINE__, pthread_self(), this->name());

  return CardGeneration::expand(bytes, expand_bytes);
}

/**
 * 扩展内存代
 */
bool CardGeneration::expand(size_t bytes, size_t expand_bytes) {
  assert_locked_or_safepoint(Heap_lock);

  if (bytes == 0) {
    return true;  // That's what grow_by(0) would return
  }

  size_t aligned_bytes  = ReservedSpace::page_align_size_up(bytes);
  if (aligned_bytes == 0){
    // The alignment caused the number of bytes to wrap.  An expand_by(0) will
    // return true with the implication that an expansion was done when it
    // was not.  A call to expand implies a best effort to expand by "bytes"
    // but not a guarantee.  Align down to give a best effort.  This is likely
    // the most that the generation can expand since it has some capacity to
    // start with.
    aligned_bytes = ReservedSpace::page_align_size_down(bytes);
  }


  size_t aligned_expand_bytes = ReservedSpace::page_align_size_up(expand_bytes);

  bool success = false;
  if (aligned_expand_bytes > aligned_bytes) {
    success = grow_by(aligned_expand_bytes);
  }

  if (!success) {
    success = grow_by(aligned_bytes);
  }

  if (!success) {
    success = grow_to_reserved();
  }

  if (PrintGC && Verbose) {
    if (success && GC_locker::is_active()) {
      gclog_or_tty->print_cr("Garbage collection disabled, expanded heap instead");
    }
  }

  return success;
}

/**
 * 扩展当前内存代的容量
 */
bool OneContigSpaceCardGeneration::grow_by(size_t bytes) {
  assert_locked_or_safepoint(ExpandHeap_lock);

  //虚拟内存管理器扩展内存
  bool result = _virtual_space.expand_by(bytes);

  if (result) {
    size_t new_word_size = heap_word_size(_virtual_space.committed_size());
    MemRegion mr(_the_space->bottom(), new_word_size);

    // Expand card table
    Universe::heap()->barrier_set()->resize_covered_region(mr);

    //扩张内存块偏移表
    _bts->resize(new_word_size);

    // Fix for bug #4668531
    if (ZapUnusedHeapArea) {
      MemRegion mangle_region(_the_space->end(),
      (HeapWord*)_virtual_space.high());
      SpaceMangler::mangle_region(mangle_region);
    }

    //更新核心内存区管理器的地址下限
    _the_space->set_end((HeapWord*)_virtual_space.high());

    // update the space and generation capacity counters
    update_counters();

    if (Verbose && PrintGC) {
      size_t new_mem_size = _virtual_space.committed_size();
      size_t old_mem_size = new_mem_size - bytes;
      gclog_or_tty->print_cr("Expanding %s from " SIZE_FORMAT "K by "
                      SIZE_FORMAT "K to " SIZE_FORMAT "K",
                      name(), old_mem_size/K, bytes/K, new_mem_size/K);
    }

  }

  return result;
}

/**
 * 将当前内存代的容量扩展到其最大值
 */
bool OneContigSpaceCardGeneration::grow_to_reserved() {
  assert_locked_or_safepoint(ExpandHeap_lock);

  bool success = true;
  //内存代预留的剩余大小
  const size_t remaining_bytes = _virtual_space.uncommitted_size();
  if (remaining_bytes > 0) {
    success = grow_by(remaining_bytes);
    DEBUG_ONLY(if (!success) warning("grow to reserved failed");)
  }

  return success;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值