Hotspot 垃圾回收之CMSCollector(三) 源码解析

     目录

1、Par_MarkFromRootsClosure

2、Par_PushOrMarkClosure

3、Par_ConcMarkingClosure

4、Marking 总结

5、preclean_mod_union_table / preclean_card_table / preclean_klasses

6、preclean / abortable_preclean

 7、CMSKeepAliveClosure

8、CMSDrainMarkingStackClosure

9、CMSPrecleanRefsYieldClosure


上一篇《Hotspot 垃圾回收之CMSCollector(二) 源码解析》重点讲解了checkpointRootsInitial和markFromRoots方法的实现,本篇继续讲解其他方法的实现。

1、Par_MarkFromRootsClosure

      Par_MarkFromRootsClosure用来处理cmsSpace的某一段内存区域在BitMap中已打标的bit,即第一遍标记中被标记成存活的对象,是CMSConcMarkingTask::do_scan_and_mark中使用的遍历器,其实现如下:

Par_MarkFromRootsClosure::Par_MarkFromRootsClosure(CMSConcMarkingTask* task,
                       CMSCollector* collector, MemRegion span,
                       CMSBitMap* bit_map,
                       OopTaskQueue* work_queue, //传入的是GC线程对应的work_queue
                       CMSMarkStack*  overflow_stack, //传入的是全局的CMSCollector的_markStack属性
                       bool should_yield): //should_yield传入的是是否同步GC
  _collector(collector),
  _whole_span(collector->_span),
  _span(span),
  _bit_map(bit_map),
  _mut(&collector->_modUnionTable),
  _work_queue(work_queue),
  _overflow_stack(overflow_stack),
  _yield(should_yield),
  _skip_bits(0),
  _task(task)
{
  assert(_work_queue->size() == 0, "work_queue should be empty");
  _finger = span.start();
  _threshold = _finger;     //clear-on-enter优化使用的
  assert(_span.contains(_finger), "Out of bounds _finger?");
}

//注意只有打标的bit才会调用此方法
bool Par_MarkFromRootsClosure::do_bit(size_t offset) {
  if (_skip_bits > 0) {
    //如果_skip_bits大于0则跳过当前位
    _skip_bits--;
    return true;
  }
  //获取该偏移量对应的地址
  HeapWord* addr = _bit_map->startWord() + offset;
  assert(_bit_map->endWord() && addr < _bit_map->endWord(),
         "address out of range");
  assert(_bit_map->isMarked(addr), "tautology");
  if (_bit_map->isMarked(addr+1)) {
    //addr+1也被标记了,说明这是一个未初始化的对象
    assert(_skip_bits == 0, "tautology");
    _skip_bits = 2;  //addr+1和addr+size都被打标了,这两个需要跳过,不做处理
    oop p = oop(addr);
    if (p->klass_or_null() == NULL) {
      // klass为null,进一步证实其未初始化,不需要遍历,返回true
      return true;
    }
  }
  scan_oops_in_oop(addr);
  return true;
}

void Par_MarkFromRootsClosure::scan_oops_in_oop(HeapWord* ptr) {
  assert(_bit_map->isMarked(ptr), "expected bit to be set");
  //校验_work_queue是空的
  assert(_work_queue->size() == 0,
         "should drain stack to limit stack usage");
  oop obj = oop(ptr);
  assert(obj->is_oop(true), "should be an oop");
  assert(_finger <= ptr, "_finger runneth ahead");
  //修改_finger属性,指向obj的end地址
  _finger = ptr + obj->size();
  assert(_finger > ptr, "we just incremented it above");

  //CMSCleanOnEnter默认为true,表示是否采用这项优化来减少脏的卡片的数量
  if (CMSCleanOnEnter && (_finger > _threshold)) {
   
    HeapWord* old_threshold = _threshold;
    //校验old_threshold是按照card_size对齐的
    assert(old_threshold == (HeapWord*)round_to(
            (intptr_t)old_threshold, CardTableModRefBS::card_size),
           "_threshold should always be card-aligned");
    //将_finger按照card_size对齐的,计算新的_threshold
    _threshold = (HeapWord*)round_to(
                   (intptr_t)_finger, CardTableModRefBS::card_size);
    MemRegion mr(old_threshold, _threshold);
    assert(!mr.is_empty(), "Control point invariant");
    assert(_span.contains(mr), "Should clear within span"); // _whole_span ??
    //清空mut中mr对应的区域
    _mut->clear_range(mr);
  }

  //获取_global_finger的地址
  HeapWord** gfa = _task->global_finger_addr();
  Par_PushOrMarkClosure pushOrMarkClosure(_collector,
                                      _span, _bit_map,
                                      _work_queue,
                                      _overflow_stack,
                                      _finger,
                                      gfa, this);
  //将obj方法_work_queue中                       
  bool res = _work_queue->push(obj);   // overflow could occur here
  assert(res, "Will hold once we use workqueues");
  while (true) {
    oop new_oop;
    //第一次遍历时就是处理上面push进的obj
    if (!_work_queue->pop_local(new_oop)) {
      //_work_queue是空的,从_overflow_stack获取待处理的oop放入_work_queue中
      if (CMSConcMarkingTask::get_work_from_overflow_stack(
            _overflow_stack, _work_queue)) {
        //_overflow_stack中获取成功,则检查是否需要yield,如果需要则执行yield  
        do_yield_check();
        continue;
      } else {  
        ////_overflow_stack中获取失败,则终止循环
        break;
      }
    }
    assert(new_oop->is_oop(true), "Oops! expected to pop an oop");
    //遍历该oop所引用的其他oop,会把这些引用的oop打标然后再放入到_work_queue或者_overflow_stack中,从而遍历他们所引用的oop,如此循环直到所有引用的oop都遍历完成
    new_oop->oop_iterate(&pushOrMarkClosure);
    //处理掉一个oop,检查是否需要yield
    do_yield_check();
  }
  assert(_work_queue->size() == 0, "tautology, emphasizing post-condition");
}

HeapWord** global_finger_addr() { return &_global_finger; }

inline void Par_MarkFromRootsClosure::do_yield_check() {
  //同步GC下_yield为false,即同步GC下不可能出现yield
//只有异步GC,_yield为true,即CMSThread执行的GC才需要yield,且必须是要求CMSThread yield,而不是前台GC被激活了
  if (ConcurrentMarkSweepThread::should_yield() &&
      !_collector->foregroundGCIsActive() &&
      _yield) {
    do_yield_work();
  }
}

void Par_MarkFromRootsClosure::do_yield_work() {
  assert(_task != NULL, "sanity");
  _task->yield();
}

2、Par_PushOrMarkClosure

     Par_PushOrMarkClosure是Par_MarkFromRootsClosure中用来遍历已标记oop所引用的其他oop的,其实现如下:

Par_PushOrMarkClosure::Par_PushOrMarkClosure(CMSCollector* collector,
                     MemRegion span,
                     CMSBitMap* bit_map,
                     OopTaskQueue* work_queue,
                     CMSMarkStack*  overflow_stack,
                     HeapWord* finger,
                     HeapWord** global_finger_addr,
                     Par_MarkFromRootsClosure* parent) :
  MetadataAwareOopClosure(collector->ref_processor()),
  _collector(collector),
  _whole_span(collector->_span),
  _span(span),
  _bit_map(bit_map),
  _work_queue(work_queue),
  _overflow_stack(overflow_stack),
  _finger(finger),
  _global_finger_addr(global_finger_addr),
  _parent(parent)
{ }

void Par_PushOrMarkClosure::do_oop(oop* p)       { Par_PushOrMarkClosure::do_oop_work(p); }
void Par_PushOrMarkClosure::do_oop(narrowOop* p) { Par_PushOrMarkClosure::do_oop_work(p); }

inline void do_oop_nv(oop* p)       { Par_PushOrMarkClosure::do_oop_work(p); }
inline void do_oop_nv(narrowOop* p) { Par_PushOrMarkClosure::do_oop_work(p); }

void Par_PushOrMarkClosure::do_oop(oop obj) {
  // Ignore mark word because we are running concurrent with mutators.
  assert(obj->is_oop_or_null(true), "expected an oop or NULL");
  HeapWord* addr = (HeapWord*)obj;
  //如果addr地址在老年代中,并且未被标记
  if (_whole_span.contains(addr) && !_bit_map->isMarked(addr)) {
    //将该oop打标,res返回false表示其他线程已经完成打标了
    bool res = _bit_map->par_mark(addr);    // now grey
    volatile HeapWord** gfa = (volatile HeapWord**)_global_finger_addr;
    
    //下列三种情况都不做任何处理
    if (   !res       //其他线程完成打标了,他们会负责打标后的处理
        || (addr >= *gfa)  //addr大于global_finger的值,后面的任务会处理该oop
        || (_span.contains(addr) && addr >= _finger)) { //该对象位于当前遍历的内存块内,后面会遍历处理的
      return;
    }
    // the bit map iteration has already either passed, or
    // sampled, this bit in the bit map; we'll need to
    // use the marking stack to scan this oop's oops.
    bool simulate_overflow = false;
    
    if (simulate_overflow ||
        //将obj先尝试加入到_work_queue中,如果满了返回false,则尝试加入到_overflow_stack中
        //如果_work_queue和_overflow_stack都满了,则返回false,有一个成功则返回true
        //加入到_work_queue和_overflow_stack中的目的是为了继续遍历该oop引用的其他oop
        !(_work_queue->push(obj) || _overflow_stack->par_push(obj))) {
      //_work_queue和_overflow_stack都满了
      if (PrintCMSStatistics != 0) {
        gclog_or_tty->print_cr("CMS marking stack overflow (benign) at "
                               SIZE_FORMAT, _overflow_stack->capacity());
      }
      assert(simulate_overflow ||
             _work_queue->size() == _work_queue->max_elems(),
            "Else push should have succeeded");
      //处理stack_overflow满了的问题,取stack_overflow中的最小地址作为_restart_addr,然后清空
      //stack_overflow中的数据并扩容,注意此时并不会执行yeild,扩容后继续遍历,等待遍历结束会重新从restart_addr处开始遍历
      handle_stack_overflow(addr);
    }
    //检查是否需要yield,如果需要则yield
    do_yield_check();
  }
}

void Par_PushOrMarkClosure::handle_stack_overflow(HeapWord* lost) {
  //获取_overflow_stack的锁
  MutexLockerEx ml(_overflow_stack->par_lock(),
                   Mutex::_no_safepoint_check_flag);
  //取_overflow_stack中地址最小的一个元素
  HeapWord* ra = (HeapWord*)_overflow_stack->least_value(lost);
  //设置_restart_addr,方便后面重新扫描
  _collector->lower_restart_addr(ra);
  //丢掉_overflow_stack中的内容,然后扩容
  _overflow_stack->reset();  // discard stack contents
  _overflow_stack->expand(); // expand the stack if possible
}

void CMSCollector::lower_restart_addr(HeapWord* low) {
  //重置_restart_addr
  assert(_span.contains(low), "Out of bounds addr");
  if (_restart_addr == NULL) {
    _restart_addr = low;
  } else {
    _restart_addr = MIN2(_restart_addr, low);
  }
}

inline void Par_PushOrMarkClosure::do_yield_check() {
  _parent->do_yield_check();
}

3、Par_ConcMarkingClosure

     Par_ConcMarkingClosure是CMSConcMarkingTask::do_work_steal中使用的遍历器,其实现跟Par_PushOrMarkClosure基本一致,就是多了一个遍历_work_queue的方法,其实现如下:

Par_ConcMarkingClosure(CMSCollector* collector, CMSConcMarkingTask* task, OopTaskQueue* work_queue,
                         CMSBitMap* bit_map, CMSMarkStack* overflow_stack):
    MetadataAwareOopClosure(collector->ref_processor()),
    _collector(collector),
    _task(task),
    _span(collector->_span),
    _work_queue(work_queue),
    _bit_map(bit_map),
    _overflow_stack(overflow_stack)
  { }

//通过DO_OOP_WORK_DEFN转换成对do_oop的调用
void Par_ConcMarkingClosure::do_oop(oop* p)       { Par_ConcMarkingClosure::do_oop_work(p); }
void Par_ConcMarkingClosure::do_oop(narrowOop* p) { Par_ConcMarkingClosure::do_oop_work(p); }

void Par_ConcMarkingClosure::trim_queue(size_t max) {
  while (_work_queue->size() > max) {
    oop new_oop;
    //从_work_queue中pop出一个oop
    if (_work_queue->pop_local(new_oop)) {
      //校验该oop是oop,被打标了,且位于span内
      assert(new_oop->is_oop(), "Should be an oop");
      assert(_bit_map->isMarked((HeapWord*)new_oop), "Grey object");
      assert(_span.contains((HeapWord*)new_oop), "Not in span");
      //遍历该oop引用的其他oop
      new_oop->oop_iterate(this);  // do_oop() above
      //检查是否需要yeild,如果需要则yield
      do_yield_check();
    }
  }
}

void do_yield_check() {
    if (_task->should_yield()) {
      _task->yield();
    }
  }

void Par_ConcMarkingClosure::do_oop(oop obj) {
  assert(obj->is_oop_or_null(true), "expected an oop or NULL");
  HeapWord* addr = (HeapWord*)obj;
  //处理逻辑和Par_PushOrMarkClosure基本一致
  if (_span.contains(addr) && !_bit_map->isMarked(addr)) {
    if (_bit_map->par_mark(addr)) {     // ... now grey
      // push on work queue (grey set)
      bool simulate_overflow = false;
      if (simulate_overflow ||
          !(_work_queue->push(obj) || _overflow_stack->par_push(obj))) {
        // stack overflow
        if (PrintCMSStatistics != 0) {
          gclog_or_tty->print_cr("CMS marking stack overflow (benign) at "
                                 SIZE_FORMAT, _overflow_stack->capacity());
        assert(simulate_overflow ||
               _work_queue->size() == _work_queue->max_elems(),
              "Else push should have succeeded");
        handle_stack_overflow(addr);
      }
    } // Else, some other thread got there first
    do_yield_check();
  }
}

void Par_ConcMarkingClosure::handle_stack_overflow(HeapWord* lost) {
  MutexLockerEx ml(_overflow_stack->par_lock(),
                   Mutex::_no_safepoint_check_flag);
  HeapWord* ra = (HeapWord*)_overflow_stack->least_value(lost);
  _collector->lower_restart_addr(ra);
  _overflow_stack->reset();  // discard stack contents
  _overflow_stack->expand(); // expand the stack if possible
}

4、Marking 总结

      Marking不要求JVM处于安全点,Marking遍历的是老年代在BitMap中对应的被打标的Bit(位),这些位就是第一个步骤InitialMarking中遍历某个对象所引用的其他对象时打标的。根据BitMap中的打标的位找到对应的对象地址,然后遍历该对象所引用的其他对象,如果其他对象中有没有在BitMap中打标的,则在BitMap中打标,并将其加入到一个跟单个GC线程绑定的对象栈中,然后以同样的方式遍历处理对象栈中的对象,不断循环直到某个对象没有引用类型属性为止。

5、preclean_mod_union_table / preclean_card_table / preclean_klasses

     卡表(CardTableModRefBS)是用一个字节表示一个卡表项,参考《Hotspot 垃圾回收之BarrierSet(一) 源码解析》,_modUnionTable是一个CMSBitMap,用一个位来表示一个卡表项,位打标了表示这个卡表项是脏的。前面两个方法的处理逻辑是高度相似的,如果修改其中一个方法,应该检查另外一个方法是否需要修改。第一个方法会从老年代对应的_modUnionTable中不断循环查找一段连续的脏的位,即发生修改的卡表项,然后遍历这段脏的位对应的内存空间中的对象。第二个方法是直接遍历老年代对应的卡表项,不断循环查找一段连续的脏的卡表项,并将这些卡表项的值重置为preclean_card,然后遍历这些卡表项对应的内存空间中包含的对象。最后一个方法preclean_klasses用来遍历所有加载的klass。这三个方法都是preclean_work的底层实现。

size_t CMSCollector::preclean_mod_union_table(
  ConcurrentMarkSweepGeneration* gen,
  ScanMarkedObjectsAgainCarefullyClosure* cl) {
  verify_work_stacks_empty();
  verify_overflow_empty();

  //获取老年代的起止地址
  HeapWord* startAddr = gen->reserved().start();
  HeapWord* endAddr   = gen->reserved().end();

  cl->setFreelistLock(gen->freelistLock());   // needed for yielding

  //numDirtyCards表示一次循环时找到的脏的卡片数量
  //cumNumDirtyCards表示整个循环期间累计的总的脏的卡片数量
  size_t numDirtyCards, cumNumDirtyCards;
  HeapWord *nextAddr, *lastAddr;
  for (cumNumDirtyCards = numDirtyCards = 0,
       nextAddr = lastAddr = startAddr;
       nextAddr < endAddr;
       nextAddr = lastAddr, cumNumDirtyCards += numDirtyCards) {

    ResourceMark rm;
    HandleMark   hm;

    MemRegion dirtyRegion;
    {
      stopTimer();
      //获取CMS Token
      CMSTokenSync ts(true);
      startTimer();
      sample_eden();
      //获取nextAddr之后的一段连续的脏的区域,并将这段区域的标记清除,注意这段区域同样位于老年代的堆内存区域中
      dirtyRegion =
        _modUnionTable.getAndClearMarkedRegion(nextAddr, endAddr);
      assert(dirtyRegion.start() >= nextAddr,
             "returned region inconsistent?");
    }
    //记录上一个dirtyRegion的终止地址,下一次查找时从该地址开始
    lastAddr = dirtyRegion.end();
    //获取对应的卡表数量
    numDirtyCards =
      _modUnionTable.heapWordDiffToOffsetDiff(dirtyRegion.word_size());

   
    if (!dirtyRegion.is_empty()) {
      assert(numDirtyCards > 0, "consistency check");
      HeapWord* stop_point = NULL;
      stopTimer();
      //获取CMS Token,freelistLock锁和bitMapLock锁
      CMSTokenSyncWithLocks ts(true, gen->freelistLock(),
                               bitMapLock());
      startTimer();
      {
        verify_work_stacks_empty();
        verify_overflow_empty();
        sample_eden();
        //遍历这一段脏的区域
        //返回的stop_point如果非空,要么是遍历时发现一个无法解析的对象的地址,要么是处于abortable preclean
        //阶段,preclean需要被中止
        stop_point =
          gen->cmsSpace()->object_iterate_careful_m(dirtyRegion, cl);
      }
      if (stop_point != NULL) {
        
        assert((_collectorState == AbortablePreclean && should_abort_preclean()),
               "Should only be AbortablePreclean.");
        //将未遍历的区域重新打标
        _modUnionTable.mark_range(MemRegion(stop_point, dirtyRegion.end()));
        if (should_abort_preclean()) {
          break; //终止preclean
        } else {
          //计算下一次遍历的起始地址
          lastAddr = next_card_start_after_block(stop_point);
        }
      }
    } else {
      //dirtyRegion是空的,即遍历完成,没有找到脏的卡表项
      assert(lastAddr == endAddr, "consistency check");
      assert(numDirtyCards == 0, "consistency check");
      break;
    }
  }
  verify_work_stacks_empty();
  verify_overflow_empty();
  return cumNumDirtyCards;
}

void CMSCollector::sample_eden() {
  //
  assert(Thread::current()->is_ConcurrentGC_thread(),
         "Only the cms thread may collect Eden samples");
  assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(),
         "Should collect samples while holding CMS token");
  if (!_start_sampling) {
    return;
  }
  //CMSEdenChunksRecordAlways为true,则_eden_chunk_array由年轻代分配内存时使用,该参数默认为true
  //所以只有在CMSEdenChunksRecordAlways为false,即年轻代不使用时,才使用,其采样逻辑不变
  if (_eden_chunk_array != NULL && !CMSEdenChunksRecordAlways) {
    if (_eden_chunk_index < _eden_chunk_capacity) {
      _eden_chunk_array[_eden_chunk_index] = *_top_addr;   // take sample
      assert(_eden_chunk_array[_eden_chunk_index] <= *_end_addr,
             "Unexpected state of Eden");
      if (_eden_chunk_index == 0 ||
          (pointer_delta(_eden_chunk_array[_eden_chunk_index],
                         _eden_chunk_array[_eden_chunk_index-1])
           >= CMSSamplingGrain)) {
        _eden_chunk_index++;  // commit sample
      }
    }
  }
  if ((_collectorState == AbortablePreclean) && !_abort_preclean) {
    size_t used = get_eden_used();
    size_t capacity = get_eden_capacity();
    assert(used <= capacity, "Unexpected state of Eden");
    //CMSScheduleRemarkEdenPenetration的默认值是50,即已使用内存超过总容量的50%时,将_abort_preclean置为true
    if (used >  (capacity/100 * CMSScheduleRemarkEdenPenetration)) {
      _abort_preclean = true;
    }
  }
}

inline size_t CMSBitMap::heapWordDiffToOffsetDiff(size_t diff) const {
  assert((diff & ((1 << _shifter) - 1)) == 0, "argument check");
  return diff >> _shifter;
}

inline bool CMSCollector::should_abort_preclean() const {
  return _collectorState == AbortablePreclean &&
         (_abort_preclean || _foregroundGCIsActive ||
          GenCollectedHeap::heap()->incremental_collection_will_fail(true /* consult_young */));
}

HeapWord* CMSCollector::next_card_start_after_block(HeapWord* addr) const {
  size_t sz = 0;
  oop p = (oop)addr;
  if (p->klass_or_null() != NULL) {
    //p是一个正常的对象地址
    sz = CompactibleFreeListSpace::adjustObjectSize(p->size());
  } else {
    //p是一个未初始化对象的地址,则根据该对象的终止地址确认对象大小
    sz = block_size_using_printezis_bits(addr);
  }
  assert(sz > 0, "size must be nonzero");
  //确定下一个遍历的内存块的地址,将该地址按照card_size对齐,获取下一次遍历的起始地址
  HeapWord* next_block = addr + sz;
  HeapWord* next_card  = (HeapWord*)round_to((uintptr_t)next_block,
                                             CardTableModRefBS::card_size);
  assert(round_down((uintptr_t)addr,      CardTableModRefBS::card_size) <
         round_down((uintptr_t)next_card, CardTableModRefBS::card_size),
         "must be different cards");
  return next_card;
}

size_t CMSCollector::block_size_using_printezis_bits(HeapWord* addr) const {
   //校验addr是一个未初始化对象地址,参考CMSCollector::direct_allocated方法
   assert(_markBitMap.isMarked(addr) && _markBitMap.isMarked(addr + 1),
          "missing Printezis mark?");
  //获取下一个打标的位的地址,即该对象的终止地址        
  HeapWord* nextOneAddr = _markBitMap.getNextMarkedWordAddress(addr + 2);
  //计算该对象的大小
  size_t size = pointer_delta(nextOneAddr + 1, addr);
  //校验该对象的大小符合规范
  assert(size == CompactibleFreeListSpace::adjustObjectSize(size),
         "alignment problem");
  assert(size >= 3, "Necessary for Printezis marks to work");
  return size;
}

size_t CMSCollector::preclean_card_table(ConcurrentMarkSweepGeneration* gen,
  ScanMarkedObjectsAgainCarefullyClosure* cl) {
  
  //preclean_mod_union_table中是通过gen->reserved()获取起止地址,这个是预先申请的一段连续地址空间,部分空间实际未分配
  //通过_virtual_space的high和low获取的地址是当前已分配内存的起止地址,其范围要小于gen->reserved()获取的
  HeapWord* endAddr   = (HeapWord*)(gen->_virtual_space.high());
  HeapWord* startAddr = (HeapWord*)(gen->_virtual_space.low());
  
  cl->setFreelistLock(gen->freelistLock());   // needed for yielding

  size_t numDirtyCards, cumNumDirtyCards;
  HeapWord *lastAddr, *nextAddr;

  for (cumNumDirtyCards = numDirtyCards = 0,
       nextAddr = lastAddr = startAddr;
       nextAddr < endAddr;
       nextAddr = lastAddr, cumNumDirtyCards += numDirtyCards) {

    ResourceMark rm;
    HandleMark   hm;

    MemRegion dirtyRegion;
    {
      stopTimer();
      //获取CMS Token
      CMSTokenSync x(true); // is cms thread
      startTimer();
      sample_eden();
      //遍历nextAddr, endAddr对应的卡表项,找到一段连续的脏的卡表项,将其重置为precleaned_card
      dirtyRegion = _ct->ct_bs()->dirty_card_range_after_reset(
                                    MemRegion(nextAddr, endAddr),
                                    true,
                                    CardTableModRefBS::precleaned_card_val());

      assert(dirtyRegion.start() >= nextAddr,
             "returned region inconsistent?");
    }
    lastAddr = dirtyRegion.end();
    //获取脏的卡表项的数量
    numDirtyCards =
      dirtyRegion.word_size()/CardTableModRefBS::card_size_in_words;

    if (!dirtyRegion.is_empty()) {
      stopTimer();
      CMSTokenSyncWithLocks ts(true, gen->freelistLock(), bitMapLock());
      startTimer();
      sample_eden();
      verify_work_stacks_empty();
      verify_overflow_empty();
      //遍历dirtyRegion中所包含的对象
      HeapWord* stop_point =
        gen->cmsSpace()->object_iterate_careful_m(dirtyRegion, cl);
      if (stop_point != NULL) {
        assert((_collectorState == AbortablePreclean && should_abort_preclean()),
               "Should only be AbortablePreclean.");
        //将剩余的未遍历区域重新标记为脏的       
        _ct->ct_bs()->invalidate(MemRegion(stop_point, dirtyRegion.end()));
        if (should_abort_preclean()) {
          break; //终止循环
        } else {
          //计算下一次开始遍历的起始地址
          lastAddr = next_card_start_after_block(stop_point);
        }
      }
    } else {
      //dirtyRegion是空的
      break;
    }
  }
  verify_work_stacks_empty();
  verify_overflow_empty();
  return cumNumDirtyCards;
}

void CMSCollector::preclean_klasses(MarkRefsIntoAndScanClosure* cl, Mutex* freelistLock) {

  cl->set_freelistLock(freelistLock);
  //获取CMS Token,freelistLock,bitMapLock锁
  CMSTokenSyncWithLocks ts(true, freelistLock, bitMapLock());
  //遍历所有加载的klass
  PrecleanKlassClosure preclean_klass_closure(cl);
  ClassLoaderDataGraph::classes_do(&preclean_klass_closure);

  verify_work_stacks_empty();
  verify_overflow_empty();
}

6、preclean / abortable_preclean

     这两个方法分别用来执行Precleaning和AbortablePreclean步骤,这两个步骤的核心逻辑都是同一个方法preclean_work,而且两者是相关联的。

void CMSCollector::preclean() {
  //校验调用线程是CMS Thread
  check_correct_thread_executing();
  assert(Thread::current()->is_ConcurrentGC_thread(), "Wrong thread");
  //校验_markStack和_overflow_list为空
  verify_work_stacks_empty();
  verify_overflow_empty();
  _abort_preclean = false;
  //CMSPrecleaningEnabled表示是否允许并行的预清理,默认为true
  if (CMSPrecleaningEnabled) {
    //CMSEdenChunksRecordAlways表示是否记录eden区分配的chunk的地址,默认为true
    if (!CMSEdenChunksRecordAlways) {
      _eden_chunk_index = 0;
    }
    size_t used = get_eden_used();
    size_t capacity = get_eden_capacity();
    //CMSScheduleRemarkSamplingRatio的默认值是5,CMSScheduleRemarkEdenPenetration的默认值是50
    //即默认情况下used小于capacity的10%的时候_start_sampling为true
    if (used < (capacity/(CMSScheduleRemarkSamplingRatio * 100)
                * CMSScheduleRemarkEdenPenetration)) {
      _start_sampling = true;
    } else {
      _start_sampling = false;
    }
    //TraceCPUTime和CMSPhaseAccounting都是记录日志使用
    TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty);
    CMSPhaseAccounting pa(this, "preclean", _gc_tracer_cm->gc_id(), !PrintGCDetails);
    //CMSPrecleanRefLists1表示在预处理环节清理Reference实例,默认为true
    //CMSPrecleanSurvivors1表示是否在预处理环节清理survivor区,默认为false
    preclean_work(CMSPrecleanRefLists1, CMSPrecleanSurvivors1);
  }
  CMSTokenSync x(true); // is cms thread
  //修改状态
  if (CMSPrecleaningEnabled) {
    sample_eden();
    _collectorState = AbortablePreclean;
  } else {
    _collectorState = FinalMarking;
  }
  verify_work_stacks_empty();
  verify_overflow_empty();
}


inline size_t CMSCollector::get_eden_used() const {
  return _young_gen->as_DefNewGeneration()->eden()->used();
}

inline size_t CMSCollector::get_eden_capacity() const {
  return _young_gen->as_DefNewGeneration()->eden()->capacity();
}

size_t CMSCollector::preclean_work(bool clean_refs, bool clean_survivor) {
  //校验状态
  assert(_collectorState == Precleaning ||
         _collectorState == AbortablePreclean, "incorrect state");
  ResourceMark rm;
  HandleMark   hm;

  ReferenceProcessor* rp = ref_processor();
  //临时设置discovery_is_mt属性为false
  ReferenceProcessorMTDiscoveryMutator rp_mut_discovery(rp, false);

  if (clean_refs) {
    CMSPrecleanRefsYieldClosure yield_cl(this);
    assert(rp->span().equals(_span), "Spans should be equal");
    CMSKeepAliveClosure keep_alive(this, _span, &_markBitMap,
                                   &_markStack, true /* preclean */);
    CMSDrainMarkingStackClosure complete_trace(this,
                                   _span, &_markBitMap, &_markStack,
                                   &keep_alive, true /* preclean */);

    stopTimer();
    //获取CMS Token 和bitMapLock锁
    CMSTokenSyncWithLocks x(true /* is cms thread */,
                            bitMapLock());
    startTimer();
    //如果CMSEdenChunksRecordAlways为false则执行eden的top地址采样
    sample_eden();

    GCTimer *gc_timer = NULL; // Currently not tracing concurrent phases
    //Reference实例的预处理
    //rp->is_alive_non_header方法返回的是CMSCollector的CMSIsAliveClosure,参考CMSCollector::ref_processor_init方法实现
    rp->preclean_discovered_references(
          rp->is_alive_non_header(), &keep_alive, &complete_trace, &yield_cl,
          gc_timer, _gc_tracer_cm->gc_id());
  }

  if (clean_survivor) {  // preclean the active survivor space(s)
    assert(_young_gen->kind() == Generation::DefNew ||
           _young_gen->kind() == Generation::ParNew ||
           _young_gen->kind() == Generation::ASParNew,
         "incorrect type for cast");
    DefNewGeneration* dng = (DefNewGeneration*)_young_gen;
    PushAndMarkClosure pam_cl(this, _span, ref_processor(),
                             &_markBitMap, &_modUnionTable,
                             &_markStack, true /* precleaning phase */);
    stopTimer();
    CMSTokenSyncWithLocks ts(true /* is cms thread */,
                             bitMapLock());
    startTimer();
    unsigned int before_count =
      GenCollectedHeap::heap()->total_collections();
    SurvivorSpacePrecleanClosure
      sss_cl(this, _span, &_markBitMap, &_markStack,
             &pam_cl, before_count, CMSYield);
    //遍历survivor区        
    dng->from()->object_iterate_careful(&sss_cl);
    dng->to()->object_iterate_careful(&sss_cl);
  }
  
  MarkRefsIntoAndScanClosure
    mrias_cl(_span, ref_processor(), &_markBitMap, &_modUnionTable,
             &_markStack, this, CMSYield,
             true /* precleaning phase */);
 
  ScanMarkedObjectsAgainCarefullyClosure
    smoac_cl(this, _span,
      &_markBitMap, &_markStack, &mrias_cl, CMSYield);

  //CMSPrecleanIter的默认值是3
  assert(CMSPrecleanIter < 10, "CMSPrecleanIter is too large");
  //CMSPrecleanNumerator的默认值是2,CMSPrecleanDenominator的默认值是3
  assert(CMSPrecleanNumerator < CMSPrecleanDenominator,
         "Bad convergence multiplier");
  //CMSPrecleanThreshold的默认值是1000       
  assert(CMSPrecleanThreshold >= 100,
         "Unreasonably low CMSPrecleanThreshold");

  //lastNumCards表示上一次遍历找到的脏的卡表数
  //curNumCards表示当前遍历找到的脏的卡表数
  //cumNumCards表示累加的找到的脏的卡表数
  size_t numIter, cumNumCards, lastNumCards, curNumCards;
  for (numIter = 0, cumNumCards = lastNumCards = curNumCards = 0;
       numIter < CMSPrecleanIter;
       numIter++, lastNumCards = curNumCards, cumNumCards += curNumCards) {
     //遍历mod_union_table中被打标的位,即遍历卡表项是脏的内存空间中的对象
    curNumCards  = preclean_mod_union_table(_cmsGen, &smoac_cl);
    if (Verbose && PrintGCDetails) {
      gclog_or_tty->print(" (modUnionTable: %d cards)", curNumCards);
    }
    
    //如果找到的脏的卡表数小于阈值 或者当前遍历找到的脏的卡表数大于上一次遍历的2/3,则终止遍历
    if (curNumCards <= CMSPrecleanThreshold ||
        (numIter > 0 &&
         (curNumCards * CMSPrecleanDenominator >
         lastNumCards * CMSPrecleanNumerator))) {
      numIter++;
      cumNumCards += curNumCards;
      break;
    }
  }
  //遍历加载的所有Klass
  preclean_klasses(&mrias_cl, _cmsGen->freelistLock());
  //遍历老年代对应的卡表区域,找到所有脏的卡表项
  curNumCards = preclean_card_table(_cmsGen, &smoac_cl);
  cumNumCards += curNumCards;
  if (PrintGCDetails && PrintCMSStatistics != 0) {
    gclog_or_tty->print_cr(" (cardTable: %d cards, re-scanned %d cards, %d iterations)",
                  curNumCards, cumNumCards, numIter);
  }
  //返回总共遍历处理的卡表项的数量
  return cumNumCards;   // as a measure of useful work done
}


void CMSCollector::abortable_preclean() {
  //校验调用线程是否正确
  check_correct_thread_executing();
  //校验CMSPrecleaningEnabled为true,该配置项默认为true
  assert(CMSPrecleaningEnabled,  "Inconsistent control state");
  assert(_collectorState == AbortablePreclean, "Inconsistent control state");

  //如果eden区已使用内存大于CMSScheduleRemarkEdenSizeThreshold,该属性默认为2M
  if (get_eden_used() > CMSScheduleRemarkEdenSizeThreshold) {
    TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty);
    CMSPhaseAccounting pa(this, "abortable-preclean", _gc_tracer_cm->gc_id(), !PrintGCDetails);
    
    size_t loops = 0, workdone = 0, cumworkdone = 0, waited = 0;
    //不断循环直到这两个条件有一个为true
    while (!(should_abort_preclean() ||
             ConcurrentMarkSweepThread::should_terminate())) {
      //CMSPrecleanRefLists2表示是否清理Reference实例,默认为false
      //CMSPrecleanSurvivors2表示是否清理Survivor区,默认为true
      //preclean_work返回累积清理的卡表项的个数      
      workdone = preclean_work(CMSPrecleanRefLists2, CMSPrecleanSurvivors2);
      cumworkdone += workdone;
      loops++;
      //CMSMaxAbortablePrecleanLoops表示循环的最大次数,默认为0
      if ((CMSMaxAbortablePrecleanLoops != 0) &&
          loops >= CMSMaxAbortablePrecleanLoops) {
        if (PrintGCDetails) {
          gclog_or_tty->print(" CMS: abort preclean due to loops ");
        }
        break;
      }
      //CMSMaxAbortablePrecleanTime表示AbortablePreclean的最大时间,默认是5000,单位毫秒
      if (pa.wallclock_millis() > CMSMaxAbortablePrecleanTime) {
        if (PrintGCDetails) {
          gclog_or_tty->print(" CMS: abort preclean due to time ");
        }
        break;
      }
      //CMSAbortablePrecleanMinWorkPerIteration默认是100,如果清理的脏的卡表数小于该值则应该等待
      if (workdone < CMSAbortablePrecleanMinWorkPerIteration) {
        stopTimer();
         //在CGC_lock上等待最多CMSAbortablePrecleanWaitMillis毫秒,该属性默认值是100
        cmsThread()->wait_on_cms_lock(CMSAbortablePrecleanWaitMillis);
        startTimer();
        waited++;
      }
    }
    if (PrintCMSStatistics > 0) {
      gclog_or_tty->print(" [%d iterations, %d waits, %d cards)] ",
                          loops, waited, cumworkdone);
    }
  }
  //退出循环,获取CMS Token,将状态置为FinalMarking
  CMSTokenSync x(true); // is cms thread
  if (_collectorState != Idling) {
    assert(_collectorState == AbortablePreclean,
           "Spontaneous state transition?");
    _collectorState = FinalMarking;
  } // Else, a foreground collection completed this CMS cycle.
  return;
}

inline size_t CMSCollector::get_eden_used() const {
  return _young_gen->as_DefNewGeneration()->eden()->used();
}

void ConcurrentMarkSweepThread::wait_on_cms_lock(long t_millis) {
  MutexLockerEx x(CGC_lock,
                  Mutex::_no_safepoint_check_flag);
  if (_should_terminate || _collector->_full_gc_requested) {
    return;
  }
  set_CMS_flag(CMS_cms_wants_token);   // to provoke notifies
  CGC_lock->wait(Mutex::_no_safepoint_check_flag, t_millis);
  clear_CMS_flag(CMS_cms_wants_token);
  assert(!CMS_flag_is_set(CMS_cms_has_token | CMS_cms_wants_token),
         "Should not be set");
}

 7、CMSKeepAliveClosure

       CMSKeepAliveClosure用来将某个oop标记成存活的,是预处理查找到的Reference实例调用的,其实现如下:

CMSKeepAliveClosure::CMSKeepAliveClosure( CMSCollector* collector,
                      MemRegion span,
                      CMSBitMap* bit_map, CMSMarkStack* mark_stack,
                      bool cpc):
  _collector(collector),
  _span(span),
  _bit_map(bit_map),
  _mark_stack(mark_stack),
  _concurrent_precleaning(cpc) { //preclean和abortable_preclean都传true
  assert(!_span.is_empty(), "Empty span could spell trouble");
}


void CMSKeepAliveClosure::do_oop(oop obj) {
  HeapWord* addr = (HeapWord*)obj;
  //如果addr在堆内存中,且未标记
  if (_span.contains(addr) &&
      !_bit_map->isMarked(addr)) {
    //将其打标  
    _bit_map->mark(addr);
    bool simulate_overflow = false;
    
    if (simulate_overflow || !_mark_stack->push(obj)) {
      //如果_mark_stack->push为false,即_mark_stack已经满了
      if (_concurrent_precleaning) {
        //_concurrent_precleaning为true,表示执行预处理,否则的话将其放到overflow_list中
        //校验_overflow_list是NULL
        assert(_collector->overflow_list_is_empty(), "Error");
        if (obj->is_objArray()) {
          size_t sz = obj->size();
          //数组的终止地址按card_size对齐
          HeapWord* end_card_addr =
            (HeapWord*)round_to((intptr_t)(addr+sz), CardTableModRefBS::card_size);
          MemRegion redirty_range = MemRegion(addr, end_card_addr);
          assert(!redirty_range.is_empty(), "Arithmetical tautology");
          //将数组对应的内存区域在_modUnionTable中打标
          _collector->_modUnionTable.mark_range(redirty_range);
        } else {
          _collector->_modUnionTable.mark(addr);
        }
        _collector->_ser_kac_preclean_ovflw++;
      } else {
         //_concurrent_precleaning为false
        _collector->push_on_overflow_list(obj);
        _collector->_ser_kac_ovflw++;
      }
    }
  }
}

void CMSCollector::push_on_overflow_list(oop p) {
  NOT_PRODUCT(_num_par_pushes++;)
  assert(p->is_oop(), "Not an oop");
  //如果对象头中包含重要信息则将其对象头和oop保存到对应的stack中
  preserve_mark_if_necessary(p);
  //通过对象头地址构成一个链表
  p->set_mark((markOop)_overflow_list);
  _overflow_list = p;
}

void CMSCollector::preserve_mark_if_necessary(oop p) {
  markOop m = p->mark();
  if (m->must_be_preserved(p)) {
    preserve_mark_work(p, m);
  }
}

void CMSCollector::preserve_mark_work(oop p, markOop m) {
  _preserved_oop_stack.push(p);
  _preserved_mark_stack.push(m);
  assert(m == p->mark(), "Mark word changed");
  assert(_preserved_oop_stack.size() == _preserved_mark_stack.size(),
         "bijection");
}

8、CMSDrainMarkingStackClosure

      CMSDrainMarkingStackClosure是某一类型的Reference实例都preclean完成后调用的,用来处理CMSKeepAliveClosure放在_overflow_list中的待处理oop,会不断的从_overflow_list中取出一定数量的oop,然后遍历该oop所引用的其他对象,通过CMSKeepAliveClosure来处理所引用的其他对象,即将该对象所引用的其他对象也都标记成存活的。


  CMSDrainMarkingStackClosure(CMSCollector* collector, MemRegion span,
                      CMSBitMap* bit_map, CMSMarkStack* mark_stack,
                      CMSKeepAliveClosure* keep_alive,
                      bool cpc):
    _collector(collector),
    _span(span),
    _bit_map(bit_map),
    _mark_stack(mark_stack),
    _keep_alive(keep_alive),
    _concurrent_precleaning(cpc) {
    assert(_concurrent_precleaning == _keep_alive->concurrent_precleaning(),
           "Mismatch");
  }

void CMSDrainMarkingStackClosure::do_void() {
  //取_mark_stack容量的4分之一作为一次从_mark_stack中取出oop的个数
  const size_t num = _mark_stack->capacity()/4;
  //如果_concurrent_precleaning为false,则CMSKeepAliveClosure会将处理的oop放到overflow_list,overflow_list肯定非空
  assert(!_concurrent_precleaning || _collector->overflow_list_is_empty(),
         "Overflow list should be NULL during concurrent phases");
  //       
  while (!_mark_stack->isEmpty() || //_mark_stack非空
         //如果_mark_stack是空的,则从overflow_list中取出最多num个放入_mark_stack中
         _collector->take_from_overflow_list(num, _mark_stack)) {
    //弹出一个待处理的oop     
    oop obj = _mark_stack->pop();
    HeapWord* addr = (HeapWord*)obj;
    assert(_span.contains(addr), "Should be within span");
    //校验在其BitMap中已打标,打标动作在CMSKeepAliveClosure中完成的
    assert(_bit_map->isMarked(addr), "Should be marked");
    assert(obj->is_oop(), "Should be an oop");
    //遍历该对象所引用的其他对象
    obj->oop_iterate(_keep_alive);
  }
}

bool CMSCollector::take_from_overflow_list(size_t num, CMSMarkStack* stack) {
  assert(stack->isEmpty(), "Expected precondition");
  assert(stack->capacity() > num, "Shouldn't bite more than can chew");
  size_t i = num;
  oop  cur = _overflow_list;
  const markOop proto = markOopDesc::prototype();
  //不断遍历直到取出了num个oop
  for (oop next; i > 0 && cur != NULL; cur = next, i--) {
    //从对象头中取出下一个待处理的oop的地址
    next = oop(cur->mark());
    //恢复初始的对象头
    cur->set_mark(proto);   // until proven otherwise
    assert(cur->is_oop(), "Should be an oop");
    //将cur放入CMSMarkStack中
    bool res = stack->push(cur);
    assert(res, "Bit off more than can chew?");
  }
  //重置_overflow_list
  _overflow_list = cur;

  return !stack->isEmpty();
}

9、CMSPrecleanRefsYieldClosure

      CMSPrecleanRefsYieldClosure是每次开始处理一个Reference实例数组时调用的,用来判断是否应该yield,如果需要则执行yield,判断是否需要终止执行,如果前台GC被激活则返回true,调用方负责终止处理。

CMSPrecleanRefsYieldClosure(CMSCollector* collector):
    _collector(collector) {}

bool CMSPrecleanRefsYieldClosure::should_return() {
  if (ConcurrentMarkSweepThread::should_yield()) {
    //如果需要CMS Thread执行yeild 则让出CPU执行
    do_yield_work();
  }
  //如果前台GC被激活则返回true,返回true以后Reference处理就会停止
  return _collector->foregroundGCIsActive();
}
发布了117 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_31865983/article/details/104179784
今日推荐