ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Hotspot 垃圾回收之ConcurrentMarkSweepGeneration(一) 源码解析

2020-01-29 17:38:11  阅读:455  来源: 互联网

标签:par capacity bytes Hotspot range ConcurrentMarkSweepGeneration mr 源码 size


   目录

         一、CardGeneration

1、 构造函数

2、expand

3、compute_new_size

二、CMSBitMap

1、构造方法 / allocate

2、mark / par_mark / mark_range / par_mark_range / mark_large_range / par_mark_large_range

3、isMarked / par_isMarked / isUnmarked /isAllClear

4、par_clear / clear_range / par_clear_range / clear_large_range / par_clear_large_range /clear_all

5、getNextMarkedWordAddress / getNextUnmarkedWordAddress / getAndClearMarkedRegion

6、iterate / dirty_range_iterate_clear 

三、CMSMarkStack

1、构造方法和allocate

2、pop / push / par_pop / par_push

3、expand

四、ChunkArray


本篇博客讲解表示CMS老年代的ConcurrentMarkSweepGeneration的相关基础类的实现。

一、CardGeneration

    CardGeneration表示一个使用卡表来标记对象修改,使用BlockOffsetArray来记录内存块的起始位置的generation,继承自Generation,定义也在generation.hpp中,新增了如下属性:

  • GenRemSet* _rs; //与其他Generation实例共享的卡表实现实例
  • BlockOffsetSharedArray* _bts; //当前Generation独享的BlockOffsetArray实现
  • size_t _shrink_factor; //老年代内存缩容的百分比,第一次是0,第二次是10,第三次是40,第四次是100,中间有一次扩容了则被重置为0,重新开始累加,避免频繁的缩容与扩容。
  • size_t _min_heap_delta_bytes; //老年代内存扩展或者缩容时的最低内存值
  • size_t _capacity_at_prologue; //GC开始时的内存容量
  • size_t _used_at_prologue; //GC开始时的内存使用量

重点关注以下方法的实现。

1、 构造函数

CardGeneration::CardGeneration(ReservedSpace rs, size_t initial_byte_size,
                               int level,
                               GenRemSet* remset) :
  Generation(rs, initial_byte_size, level), _rs(remset),
  _shrink_factor(0), _min_heap_delta_bytes(), _capacity_at_prologue(),
  _used_at_prologue()
{
  HeapWord* start = (HeapWord*)rs.base();
  size_t reserved_byte_size = rs.size();
  //start地址和reserved_byte_size必须是4的整数倍
  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
  _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");

  //校验start地址和end地址都对应某个卡表项的起始地址
  guarantee(_rs->is_aligned(reserved_mr.start()), "generation must be card aligned");
  if (reserved_mr.end() != Universe::heap()->reserved_region().end()) {
    guarantee(_rs->is_aligned(reserved_mr.end()), "generation must be card aligned");
  }
  //MinHeapDeltaBytes的取值是128k,表示扩展时的最低内存
  _min_heap_delta_bytes = MinHeapDeltaBytes;
  _capacity_at_prologue = initial_byte_size;
  _used_at_prologue = 0;
}

2、expand

      expand用于将内存扩展,第一个参数表示期望扩展的内存空间,第二个参数表示期望扩展的最低内存空间,如果扩展了一部分内存空间,即使小于最低内存空间,则返回true,底层会调用grow_by完成扩展,如果扩展失败则尝试grow_to_reserved。其实现如下:

//第一个参数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){
    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) {
    //扩展内存空间,正常来说aligned_bytes大于aligned_expand_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_and_needs_gc()) {
      gclog_or_tty->print_cr("Garbage collection disabled, expanded heap instead");
    }
  }

  return success;
}

调用链如下:

3、compute_new_size

      该方法是在GC结束后根据参数MinHeapFreeRatio和MaxHeapFreeRatio以及当前内存的使用量来重新计算期望的容量,并做适当的扩容或者缩容处理,其实现如下:

void CardGeneration::compute_new_size() {
  assert(_shrink_factor <= 100, "invalid shrink factor");
  size_t current_shrink_factor = _shrink_factor;
  //将_shrink_factor置为0,后面缩容时会重新赋值
  _shrink_factor = 0;

  //MinHeapFreeRatio表示老年代空闲内存占总内存的最低百分比,默认值是80
  //计算最低的空闲百分比和最大的已使用百分比
  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));
  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) {
    //当前容量小于期望的容量,需要扩容
    //计算期望扩容的量
    size_t expand_bytes = minimum_desired_capacity - capacity_after_gc;
    if (expand_bytes >= _min_heap_delta_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;
  }

  //当前容量大于期望的容量,需要缩容
  size_t shrink_bytes = 0;
  //计算缩容的容量
  size_t max_shrink_bytes = capacity_after_gc - minimum_desired_capacity;

  //MaxHeapFreeRatio表示空闲堆内存的最大百分比,默认是70%,用来避免缩容,通常用于老年代,但是G1和ParallelGC下应用于整个堆
  if (MaxHeapFreeRatio < 100) {
    //根据MaxHeapFreeRatio和used_after_gc计算期望的最大内存容量
    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");

    if (capacity_after_gc > maximum_desired_capacity) {
      //计算需要缩容的内存容量
      shrink_bytes = capacity_after_gc - maximum_desired_capacity;
      //为了避免一次调用就缩容到初始大小,所以设置了_shrink_factor,第一次调用实际不缩容,第二次缩容10%,第三次40%,第四次100%,如果中间有一次扩容,则被重置为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后老年代的容量变大了,这可能是因为在promote的过程中扩展了,缩容时候需要考虑这一部分内存
    size_t expansion_for_promotion = capacity_after_gc - _capacity_at_prologue;
    expansion_for_promotion = MIN2(expansion_for_promotion, max_shrink_bytes);
    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);
    }
  }
  //需要缩容的内存大于最低要求,则执行缩容
  if (shrink_bytes >= _min_heap_delta_bytes) {
    shrink(shrink_bytes);
  }
}

其调用链如下:

二、CMSBitMap

     CMSBitMap的定义在hotspot\src\share\vm\gc_implementation\concurrentMarkSweep\concurrentMarkSweepGeneration.hpp中,表示一个通用的CMS下的BitMap,可以用于CMS的标记BitMap和mod union table,两种场景都是使用其中的一部分方法,这个类的实现是基于BitMap类的,两种场景下的shifter属性不同。其包含的属性如下:

  •   HeapWord* _bmStartWord;   //所对应的内存区域的起始地址
  •   size_t    _bmWordSize;    //所对应的内存区域的大小,单位是字宽
  •   const int _shifter;       // shifts to convert HeapWord to bit position
  •   VirtualSpace _virtual_space; // bitMap本身使用的一段连续地址空间
  •   BitMap    _bm;            // 依赖的BitMap    实例
  •   Mutex* const _lock;       // 操作bm需要的锁

重点关注以下方法的实现。

1、构造方法 / allocate

      构造方法主要是初始化lock和shifter属性,其他属性都是在allocate方法中完成初始化的,其实现如下:

CMSBitMap::CMSBitMap(int shifter, int mutex_rank, const char* mutex_name):
  _bm(),
  _shifter(shifter),
  _lock(mutex_rank >= 0 ? new Mutex(mutex_rank, mutex_name, true) : NULL)
{
  _bmStartWord = 0;
  _bmWordSize  = 0;
}

bool CMSBitMap::allocate(MemRegion mr) {
  _bmStartWord = mr.start();
  _bmWordSize  = mr.word_size();
  //为bitMap申请指定大小的一段连续地址段
  ReservedSpace brs(ReservedSpace::allocation_align_size_up(
                     (_bmWordSize >> (_shifter + LogBitsPerByte)) + 1));
  if (!brs.is_reserved()) {
    //分配失败
    warning("CMS bit map allocation failure");
    return false;
  }
  //初始化_virtual_space
  if (!_virtual_space.initialize(brs, brs.size())) {
    warning("CMS bit map backing store failure");
    return false;
  }
  assert(_virtual_space.committed_size() == brs.size(),
         "didn't reserve backing store for all of CMS bit map?");
  //设置bitMap的映射起始地址
  _bm.set_map((BitMap::bm_word_t*)_virtual_space.low());
  assert(_virtual_space.committed_size() << (_shifter + LogBitsPerByte) >=
         _bmWordSize, "inconsistency in bit map sizing");
  //设置大小
  _bm.set_size(_bmWordSize >> _shifter);

  // bm.clear(); // can we rely on getting zero'd memory? verify below
  assert(isAllClear(),
         "Expected zero'd memory from ReservedSpace constructor");
  assert(_bm.size() == heapWordDiffToOffsetDiff(sizeInWords()),
         "consistency check");
  return true;
}

其调用链如下:

 

2、mark / par_mark / mark_range / par_mark_range / mark_large_range / par_mark_large_range

     mark和par_mark是将某个地址在BitMap中对应的位打标,mark_range和par_mark_range是将某个小范围的地址区间在BitMap中对应的位打标,mark_large_range 和 par_mark_large_range将某个大范围的地址区间在BitMap中对应的位打标,通常起始地址大于32位,其实现如下:

inline void CMSBitMap::mark(HeapWord* addr) {
  //校验已经获取了锁
  assert_locked();
  //校验addr在CMSBitMap对应的地址范围内
  assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize),
         "outside underlying space?");
  _bm.set_bit(heapWordToOffset(addr));
}

inline bool CMSBitMap::par_mark(HeapWord* addr) {
  assert_locked();
  assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize),
         "outside underlying space?");
  return _bm.par_at_put(heapWordToOffset(addr), true);
}

inline size_t CMSBitMap::heapWordToOffset(HeapWord* addr) const {
  return (pointer_delta(addr, _bmStartWord)) >> _shifter;
}

inline void BitMap::set_bit(idx_t bit) {
  verify_index(bit);
  *word_addr(bit) |= bit_mask(bit);
}

bool BitMap::par_at_put(idx_t bit, bool value) {
  //par_set_bit与set_bit的区别在于使用cmpxchg_ptr改变指定地址的值,返回true表示修改成功,返回false表示其他线程完成了修改
  //底层实现都是将bit映射至bitMap中对应的地址上,然后修改指定的位
  return value ? par_set_bit(bit) : par_clear_bit(bit);
}

inline void CMSBitMap::mark_range(MemRegion mr) {
  NOT_PRODUCT(region_invariant(mr));
  //通过heapWordToOffset算出来的起始地址通常只相差一位
  _bm.set_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
                BitMap::small_range);
}

inline void CMSBitMap::par_mark_range(MemRegion mr) {
  NOT_PRODUCT(region_invariant(mr));
  // Range size is usually just 1 bit.
  _bm.par_set_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
                    BitMap::small_range);
}

inline void CMSBitMap::mark_large_range(MemRegion mr) {
  NOT_PRODUCT(region_invariant(mr));
  //通过heapWordToOffset算出来的起始地址通常只相差至少32位
  _bm.set_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
                BitMap::large_range);
}

inline void CMSBitMap::par_mark_large_range(MemRegion mr) {
  NOT_PRODUCT(region_invariant(mr));
  // Range size must be greater than 32 bytes.
  _bm.par_set_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
                    BitMap::large_range);
}

void CMSBitMap::region_invariant(MemRegion mr)
{
  assert_locked();
  // mr = mr.intersection(MemRegion(_bmStartWord, _bmWordSize));
  assert(!mr.is_empty(), "unexpected empty region");
  assert(covers(mr), "mr should be covered by bit map");
  // convert address range into offset range
  size_t start_ofs = heapWordToOffset(mr.start());
  //校验mr的结束地址已经对齐了
  assert(mr.end() == (HeapWord*)round_to((intptr_t)mr.end(),
                        ((intptr_t) 1 << (_shifter+LogHeapWordSize))),
         "Misaligned mr.end()");
  size_t end_ofs   = heapWordToOffset(mr.end());
  //校验结束地址大于起始地址
  assert(end_ofs > start_ofs, "Should mark at least one bit");
}

//判断mr是否在指BitMap对应的内存区域中
bool CMSBitMap::covers(MemRegion mr) const {
  // assert(_bm.map() == _virtual_space.low(), "map inconsistency");
  assert((size_t)_bm.size() == (_bmWordSize >> _shifter),
         "size inconsistency");
  return (mr.start() >= _bmStartWord) &&
         (mr.end()   <= endWord());
}

HeapWord* endWord()     const { return _bmStartWord + _bmWordSize; }

inline void BitMap::set_range(idx_t beg, idx_t end, RangeSizeHint hint) {
  if (hint == small_range && end - beg == 1) {
    set_bit(beg);
  } else {
    if (hint == large_range) {
      set_large_range(beg, end);
    } else {
      set_range(beg, end);
    }
  }
}

3、isMarked / par_isMarked / isUnmarked /isAllClear

      isMarked 和par_isMarked 用于判断某个地址是否已经打标,isUnmarked与之相反,是否未打标,isAllClear用于判断是否所有的标志都被清除了,其实现如下:

inline bool CMSBitMap::isMarked(HeapWord* addr) const {
  assert_locked();
  assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize),
         "outside underlying space?");
  return _bm.at(heapWordToOffset(addr));
}

inline bool CMSBitMap::par_isMarked(HeapWord* addr) const {
  //与isMarked相比,不需要检查锁
  assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize),
         "outside underlying space?");
  return _bm.at(heapWordToOffset(addr));
}


inline bool CMSBitMap::isUnmarked(HeapWord* addr) const {
  assert_locked();
  assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize),
         "outside underlying space?");
  return !_bm.at(heapWordToOffset(addr));
}

bool at(idx_t index) const {
    verify_index(index);
    //判断BitMap中对应的映射地址的对应位是否是1,如果是1,1!=0返回true,表示已经被标记了
    return (*word_addr(index) & bit_mask(index)) != 0;
  }

//返回在BitMap中对应的映射地址,64位下一个地址有8字节,64位,类似于HashMap中的一个槽位
bm_word_t* word_addr(idx_t bit) const { return map() + word_index(bit); }

//将偏移量进一步右移6位,LogBitsPerByte在64位下都是3,LogBitsPerWord是6
//右移6位丢失的精度通过bit_mask补回来
static idx_t word_index(idx_t bit)  { return bit >> LogBitsPerWord; }

//返回的值实际是2的整数倍,就64位中只有1位是1,其他的都是0
static bm_word_t bit_mask(idx_t bit) { return (bm_word_t)1 << bit_in_word(bit); }

//BitsPerWord在64位下是64,这里实际是bit对64取余
static idx_t bit_in_word(idx_t bit) { return bit & (BitsPerWord - 1); }

inline size_t CMSBitMap::heapWordToOffset(HeapWord* addr) const {
  //pointer_delta算出addr相对于起始地址的偏移量,单位是字节
  return (pointer_delta(addr, _bmStartWord)) >> _shifter;
}

inline bool CMSBitMap::isAllClear() const {
  assert_locked();
  //获取下一个被标记的地址,如果该地址大于等于结束地址,则认为所有的打标都清空了
  return getNextMarkedWordAddress(startWord()) >= endWord();
}

 结合上面打标方法的分析可知,BitMap的实现是基于对象地址都是按照8字节,即一个字宽大小对齐的,所谓打标就是将某个对象地址映射成BitMap内存的某个地址上的某个位,某个地址类似于HashMap中的一个槽,然后将这个位上的值置为1。

4、par_clear / clear_range / par_clear_range / clear_large_range / par_clear_large_range /clear_all

    par_clear用于清除某个地址在BitMap中对应的标志,clear_range和par_clear_range用于清除某个小范围的地址区间在BitMap中对应的位的标志,clear_large_range和par_clear_large_range用于清除某个大范围的地址区间在BitMap中对应的位的标志,clear_all用于清空BitMap中所有的标志,其实现如下:

inline void CMSBitMap::par_clear(HeapWord* addr) {
  assert_locked();
  assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize),
         "outside underlying space?");
  _bm.par_at_put(heapWordToOffset(addr), false);
}

inline void CMSBitMap::clear_range(MemRegion mr) {
  NOT_PRODUCT(region_invariant(mr));
  // Range size is usually just 1 bit.
  _bm.clear_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
                  BitMap::small_range);
}

inline void CMSBitMap::par_clear_range(MemRegion mr) {
  NOT_PRODUCT(region_invariant(mr));
  // Range size is usually just 1 bit.
  _bm.par_clear_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
                      BitMap::small_range);
}

inline void CMSBitMap::clear_large_range(MemRegion mr) {
  NOT_PRODUCT(region_invariant(mr));
  // Range size must be greater than 32 bytes.
  _bm.clear_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
                  BitMap::large_range);
}

inline void CMSBitMap::par_clear_large_range(MemRegion mr) {
  NOT_PRODUCT(region_invariant(mr));
  // Range size must be greater than 32 bytes.
  _bm.par_clear_range(heapWordToOffset(mr.start()), heapWordToOffset(mr.end()),
                      BitMap::large_range);
}

inline void BitMap::clear_range(idx_t beg, idx_t end, RangeSizeHint hint) {
  if (hint == small_range && end - beg == 1) {
    clear_bit(beg);
  } else {
    if (hint == large_range) {
      clear_large_range(beg, end);
    } else {
      clear_range(beg, end);
    }
  }
}

inline void CMSBitMap::clear_all() {
  assert_locked();
  // CMS bitmaps are usually cover large memory regions
  _bm.clear_large();
  return;
}

5、getNextMarkedWordAddress / getNextUnmarkedWordAddress / getAndClearMarkedRegion

     三个方法都有两个重载版本,指定起止地址和只指定起始地址,结束地址默认BitMap的结束地址。getNextMarkedWordAddress返回指定地址范围的第一个位等于1的地址,getNextUnmarkedWordAddress返回指定地址范围的第一个位等于0的地址,getAndClearMarkedRegion用于清除指定地址范围内第一个被连续打标的区域的标志,其实现如下:


inline HeapWord* CMSBitMap::getNextMarkedWordAddress(HeapWord* addr) const {
  return getNextMarkedWordAddress(addr, endWord());
}

inline HeapWord* CMSBitMap::getNextMarkedWordAddress(
  HeapWord* start_addr, HeapWord* end_addr) const {
  assert_locked();
  //找到在指定地址范围内位下一个等于1的地址
  size_t nextOffset = _bm.get_next_one_offset(
                        heapWordToOffset(start_addr),
                        heapWordToOffset(end_addr));
  //将BitMap中的地址转换成实际地址                      
  HeapWord* nextAddr = offsetToHeapWord(nextOffset);
  assert(nextAddr >= start_addr &&
         nextAddr <= end_addr, "get_next_one postcondition");
  assert((nextAddr == end_addr) ||
         isMarked(nextAddr), "get_next_one postcondition");
  return nextAddr;
}

inline HeapWord* CMSBitMap::offsetToHeapWord(size_t offset) const {
  return _bmStartWord + (offset << _shifter);
}

inline HeapWord* CMSBitMap::getNextUnmarkedWordAddress(HeapWord* addr) const {
  return getNextUnmarkedWordAddress(addr, endWord());
}

inline HeapWord* CMSBitMap::getNextUnmarkedWordAddress(
  HeapWord* start_addr, HeapWord* end_addr) const {
  assert_locked();
   //找到在指定地址范围内位下一个等于0的地址
  size_t nextOffset = _bm.get_next_zero_offset(
                        heapWordToOffset(start_addr),
                        heapWordToOffset(end_addr));
  //将BitMap中的地址转换成实际地址                         
  HeapWord* nextAddr = offsetToHeapWord(nextOffset);
  assert(nextAddr >= start_addr &&
         nextAddr <= end_addr, "get_next_zero postcondition");
  assert((nextAddr == end_addr) ||
          isUnmarked(nextAddr), "get_next_zero postcondition");
  return nextAddr;
}

inline MemRegion CMSBitMap::getAndClearMarkedRegion(HeapWord* addr) {
  return getAndClearMarkedRegion(addr, endWord());
}

inline MemRegion CMSBitMap::getAndClearMarkedRegion(HeapWord* start_addr,
                                                    HeapWord* end_addr) {
  HeapWord *start, *end;
  assert_locked();
  //找到start_addr后第一个打标的地址
  start = getNextMarkedWordAddress  (start_addr, end_addr);
  //找到start之后的第一个没有打标的地址,start和end之间的区域就是一段连续打标的区域
  end   = getNextUnmarkedWordAddress(start,      end_addr);
  assert(start <= end, "Consistency check");
  MemRegion mr(start, end);
  if (!mr.is_empty()) {
    //将start和end之间的标志去掉
    clear_range(mr);
  }
  return mr;
}

 6、iterate / dirty_range_iterate_clear 

      这两个方法都有两个重载版本,一个指定起止地址范围里,一个在整个BitMap对应的地址范围内,都是用来遍历指定地址范围内打标的位,会将这些位转换成真实地址,然后再对真实地址做必要的处理,其实现如下:

inline void CMSBitMap::iterate(BitMapClosure* cl, HeapWord* left,
                            HeapWord* right) {
  assert_locked();
  left = MAX2(_bmStartWord, left);
  right = MIN2(_bmStartWord + _bmWordSize, right);
  if (right > left) {
    //遍历逻辑封装在bm里面,BitMapClosure会自动将BitMap的映射地址转换成真实地址
    _bm.iterate(cl, heapWordToOffset(left), heapWordToOffset(right));
  }
}

void iterate(BitMapClosure* cl) {
    _bm.iterate(cl);
  }

void CMSBitMap::dirty_range_iterate_clear(MemRegion mr, MemRegionClosure* cl) {
  HeapWord *next_addr, *end_addr, *last_addr;
  assert_locked();
  assert(covers(mr), "out-of-range error");
  for (next_addr = mr.start(), end_addr = mr.end();
       next_addr < end_addr; next_addr = last_addr) {
    //找到BitMap中下一个连续的被打标的区域   
    MemRegion dirty_region = getAndClearMarkedRegion(next_addr, end_addr);
    last_addr = dirty_region.end();
    if (!dirty_region.is_empty()) {
      //执行遍历
      cl->do_MemRegion(dirty_region);
    } else {
      assert(last_addr == end_addr, "program logic");
      return;
    }
  }
}

三、CMSMarkStack

     CMSMarkStack是一个基于可扩容数组实现的保存oop的栈,其定义同样在concurrentMarkSweepGeneration.hpp中,包含的属性如下:

  •   VirtualSpace _virtual_space;  // 对应的内存区域
  •   oop*   _base;      // oop数组的基地址
  •   size_t _index;     //Stack中元素的个数
  •   size_t _capacity;  // 允许的最大容量
  •   Mutex  _par_lock;  // 并发操作base的锁
  •   size_t _hit_limit;      // 记录MarkStack的容量达到最大值的次数
  •   size_t _failed_double;  // 记录MarkStack扩容失败的次数

重点关注以下方法的实现。

1、构造方法和allocate

     这两个都是用来初始化CMSMarkStack的,其实现如下:

CMSMarkStack():
    _par_lock(Mutex::event, "CMSMarkStack._par_lock", true),
    _hit_limit(0),
    _failed_double(0) {}


bool CMSMarkStack::allocate(size_t size) {
  //按照size个oop大小申请内存,oop实际是oopDesc*的别名
  ReservedSpace rs(ReservedSpace::allocation_align_size_up(
                   size * sizeof(oop)));
  if (!rs.is_reserved()) {
    //申请内存失败
    warning("CMSMarkStack allocation failure");
    return false;
  }
  //初始化_virtual_space
  if (!_virtual_space.initialize(rs, rs.size())) {
    warning("CMSMarkStack backing store failure");
    return false;
  }
  assert(_virtual_space.committed_size() == rs.size(),
         "didn't reserve backing store for all of CMS stack?");
  //将申请内存的基地址作为base数组的起始地址       
  _base = (oop*)(_virtual_space.low());
  _index = 0;
  _capacity = size;
  NOT_PRODUCT(_max_depth = 0);
  return true;
}

其调用链如下:

 

2、pop / push / par_pop / par_push

     pop就是弹出栈顶oop,push就是把oop压入栈顶,par版本就是多了一步获取锁的动作,其实现如下:

oop pop() {
    if (!isEmpty()) {
      //index先减1,在返回减一后的index对应的oop
      return _base[--_index] ;
    }
    return NULL;
  }

bool isEmpty() const { return _index == 0; }

bool push(oop ptr) {
    if (isFull()) {
      return false;
    } else {
      //先将index处的元素置为ptr,再将index加1
      _base[_index++] = ptr;
      NOT_PRODUCT(_max_depth = MAX2(_max_depth, _index));
      return true;
    }
  }

bool isFull()  const {
    assert(_index <= _capacity, "buffer overflow");
    return _index == _capacity;
  }

oop par_pop() {
    MutexLockerEx x(&_par_lock, Mutex::_no_safepoint_check_flag);
    return pop();
  }

bool par_push(oop ptr) {
    MutexLockerEx x(&_par_lock, Mutex::_no_safepoint_check_flag);
    return push(ptr);
  }

 3、expand

      expand方法用于扩容,不同于同样基于数组实现的ArrayList的扩容,这里只是重新申请了两倍MarkStack原来对应的内存区域,原来的内存区域直接释放了,其实现如下:

void CMSMarkStack::expand() {
  //MarkStackSizeMax表示MarkStack的最大大小,64位下默认是512M
  assert(_capacity <= MarkStackSizeMax, "stack bigger than permitted");
  if (_capacity == MarkStackSizeMax) {
    //如果达到最大容量了
    if (_hit_limit++ == 0 && !CMSConcurrentMTEnabled && PrintGCDetails) {
      gclog_or_tty->print_cr(" (benign) Hit CMSMarkStack max size limit");
    }
    return;
  }
  //扩容一倍
  size_t new_capacity = MIN2(_capacity*2, MarkStackSizeMax);
  //申请内存
  ReservedSpace rs(ReservedSpace::allocation_align_size_up(
                   new_capacity * sizeof(oop)));
  if (rs.is_reserved()) {
    //释放原来的内存
    _virtual_space.release();
    //重试初始化
    if (!_virtual_space.initialize(rs, rs.size())) {
      fatal("Not enough swap for expanded marking stack");
    }
    //重置属性
    _base = (oop*)(_virtual_space.low());
    _index = 0;
    _capacity = new_capacity;
  } else if (_failed_double++ == 0 && !CMSConcurrentMTEnabled && PrintGCDetails) {
    //申请内存失败
    gclog_or_tty->print(" (benign) Failed to expand marking stack from " SIZE_FORMAT "K to "
            SIZE_FORMAT "K",
            _capacity / K, new_capacity / K);
  }
}

其调用链如下:

四、ChunkArray

      ChunkArray表示一个保存Chunk地址的数组,其定义同样在在concurrentMarkSweepGeneration.hpp中,包含如下属性:

  •  size_t _index; //ChunkArray中包含的Chunk地址的个数
  •   size_t _capacity; //ChunkArray的最大容量
  •   size_t _overflows; //达到ChunkArray容量的次数
  •   HeapWord** _array;   // 保存Chunk地址的数组基地址

重点关注以下方法的实现:

ChunkArray(HeapWord** a, size_t c):
    _index(0), _capacity(c), _overflows(0), _array(a) {}

HeapWord* nth(size_t n) {
    //返回索引为n的地址
    assert(n < end(), "Out of bounds access");
    return _array[n];
  }

void record_sample(HeapWord* p, size_t sz) {
    //将Chunk地址p保存到数组中
    if (_index < _capacity) {
      _array[_index++] = p;
    } else {
      ++_overflows;
      assert(_index == _capacity,
             err_msg("_index (" SIZE_FORMAT ") > _capacity (" SIZE_FORMAT
                     "): out of bounds at overflow#" SIZE_FORMAT,
                     _index, _capacity, _overflows));
    }
  }

 

孙大圣666 发布了109 篇原创文章 · 获赞 8 · 访问量 1万+ 私信 关注

标签:par,capacity,bytes,Hotspot,range,ConcurrentMarkSweepGeneration,mr,源码,size
来源: https://blog.csdn.net/qq_31865983/article/details/104099460

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有