Hotspot 垃圾回收之BarrierSet(二) 源码解析

ぃ、小莉子 提交于 2019-12-28 20:18:41

   目录

一、CardTableExtension 

1、定义

2、resize_covered_region

二、G1SATBCardTableLoggingModRefBS

1、write_ref_field_pre_work  /write_ref_array_pre

2、set_card_claimed /g1_mark_as_young /mark_card_deferred

三、G1SATBCardTableLoggingModRefBS

1、构造方法和initialize

2、G1SATBCardTableLoggingModRefBSChangedListener

3、write_ref_field_work /write_region_work /write_ref_array_work /invalidate

4、resize_covered_region


 本篇继续上一篇《Hotspot 垃圾回收之BarrierSet(一) 源码解析》探讨BarrierSet其他的子类的实现。

一、CardTableExtension 

1、定义

       CardTableExtension的定义在hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.hpp中,该类继承自CardTableModRefBS,是为ParallelScavengeHeap定制的卡表实现,参考其构造方法的调用链,如下:

 CardTableExtension添加了枚举ExtendedCardValue,表示扩展的CardValue,如下:

 CardTableExtension添加了一个新的用于并行遍历卡表的scavenge_contents_parallel方法,该方法的调用链如下:

因为涉及ParallelScavengeHeap的实现细节,这里暂时不深入研究了,重点关注重写的一个方法resize_covered_region的实现。

2、resize_covered_region

     resize_covered_region方法用于调整MemRegion对应的_covered和_committed元素,当前实现下_committed元素会随着MemRegion扩容而扩容,因为安全问题不会随着MemRegion缩容而缩容,_covered元素无论扩容和缩容都跟原MemRegion保持一致。该方法假定MemRegion要么是新增的,要么就是起始地址不变,终止地址改变,要么就是终止地址不变,起始地址改变这三种情形,父类的实现只支持新增和起始地址不变两种,子类新增了终止地址不变的支持。源码说明如下:

//resize_covered_region方法假定MemRegion要么是新增的,要么就是起始地址不变,终止地址改变,要么就是终止地址不变,起始地址改变
//这三种情形,父类的实现只支持新增和起始地址不变两种,子类新增了终止地址不变的支持
void CardTableExtension::resize_covered_region(MemRegion new_region) {

  for (int i = 0; i < _cur_covered_regions; i++) {
    if (_covered[i].start() == new_region.start()) {
      //找到一个基地址相同的_covered元素,说明new_region是终止地址发生改变了,调用父类实现即可
      resize_covered_region_by_start(new_region);
      return;
    }
    if (_covered[i].start() > new_region.start()) {
      break;
    }
  }
  
  //没有找到一个基地址相同的_covered元素

  int changed_region = -1;
  for (int j = 0; j < _cur_covered_regions; j++) {
    if (_covered[j].end() == new_region.end()) {
      //找到一个终止地址相同的_covered元素,即基地址在变化
      changed_region = j;
      assert(changed_region != -1, "Don't expect to add a covered region");
      //校验两者的大小不一致
      assert(_covered[changed_region].byte_size() != new_region.byte_size(),
        "The sizes should be different here");
      resize_covered_region_by_end(changed_region, new_region);
      return;
    }
  }
  // 没有一个基地址或者终止地址相同的_covered元素,说明new_region是一个全新的,
  //需要添加一个新的_covered元素,调用父类实现即可
  assert(_cur_covered_regions < _max_covered_regions,
    "An existing region should have been found");
  resize_covered_region_by_start(new_region);
}

//基地址不变,终止地址变了,通过父类的resize_covered_region修改
void CardTableExtension::resize_covered_region_by_start(MemRegion new_region) {
  CardTableModRefBS::resize_covered_region(new_region);
  debug_only(verify_guard();)
}

void CardTableExtension::resize_covered_region_by_end(int changed_region,
                                                      MemRegion new_region) {
  //当前在安全点上,只有在GC的时候才能扩展                                                    
  assert(SafepointSynchronize::is_at_safepoint(),
    "Only expect an expansion at the low end at a GC");
  debug_only(verify_guard();)

  //执行扩容或者缩容,返回true表示扩容
  if (resize_commit_uncommit(changed_region, new_region)) {
    //如果执行了扩容则更新对应commited元素
    resize_update_committed_table(changed_region, new_region);
  }

  //如果扩容了,将扩容内存对应的卡表项置为clean_card
  resize_update_card_table_entries(changed_region, new_region);

  //更新covered元素,重新排序
  resize_update_covered_table(changed_region, new_region);

  //打印日志
  if (TraceCardTableModRefBS) {
    int ind = changed_region;
    gclog_or_tty->print_cr("CardTableModRefBS::resize_covered_region: ");
    gclog_or_tty->print_cr("  "
                  "  _covered[%d].start(): " INTPTR_FORMAT
                  "  _covered[%d].last(): " INTPTR_FORMAT,
                  ind, p2i(_covered[ind].start()),
                  ind, p2i(_covered[ind].last()));
    gclog_or_tty->print_cr("  "
                  "  _committed[%d].start(): " INTPTR_FORMAT
                  "  _committed[%d].last(): " INTPTR_FORMAT,
                  ind, p2i(_committed[ind].start()),
                  ind, p2i(_committed[ind].last()));
    gclog_or_tty->print_cr("  "
                  "  byte_for(start): " INTPTR_FORMAT
                  "  byte_for(last): " INTPTR_FORMAT,
                  p2i(byte_for(_covered[ind].start())),
                  p2i(byte_for(_covered[ind].last())));
    gclog_or_tty->print_cr("  "
                  "  addr_for(start): " INTPTR_FORMAT
                  "  addr_for(last): " INTPTR_FORMAT,
                  p2i(addr_for((jbyte*) _committed[ind].start())),
                  p2i(addr_for((jbyte*) _committed[ind].last())));
  }
  debug_only(verify_guard();)
}

//判断是否缩容或者扩容,如果扩容则返回true,扩容只完成了内存commit而已,并未更新对应的covered和commited元素
//当前实现缩容不安全,没有实际缩容
bool CardTableExtension::resize_commit_uncommit(int changed_region,
                                                MemRegion new_region) {
  bool result = false;
  MemRegion cur_committed = _committed[changed_region];
  assert(_covered[changed_region].end() == new_region.end(),
    "The ends of the regions are expected to match");
  
  //如果changed_region之前的commit元素与该元素的内存区域有重叠则返回之前的commit元素的起始地址
  HeapWord* min_prev_start = lowest_prev_committed_start(changed_region);
  if (min_prev_start < cur_committed.start()) {
    //说明存在两个_committed元素的内存区域有重叠的情形,需要将这些重叠的合并
    MemRegion new_committed =
        MemRegion(min_prev_start, cur_committed.end());
    cur_committed = new_committed;
  }

  //获取new_region起始地址对应的卡表项地址,并做内存对齐
  jbyte* new_start = byte_for(new_region.start());
  HeapWord* new_start_aligned =
    (HeapWord*)align_size_down((uintptr_t)new_start, os::vm_page_size());

  
  if (new_start_aligned < cur_committed.start()) {
    //获取终止地址,不能超过_guard_region
    HeapWord* new_end_for_commit =
      MIN2(cur_committed.end(), _guard_region.start());

    if(new_start_aligned < new_end_for_commit) {
      //执行commit扩展内存
      MemRegion new_committed =
        MemRegion(new_start_aligned, new_end_for_commit);
      os::commit_memory_or_exit((char*)new_committed.start(),
                                new_committed.byte_size(), !ExecMem,
                                "card table expansion");
    }
    result = true;
  } else if (new_start_aligned > cur_committed.start()) {
    // 需要执行uncommit缩容,因为上面执行了cur_committed的合并,所以缩容的区域有可能属于另外一个MemRegion,不安全
    assert(!result, "Should be false with current workaround");
  }
  assert(_committed[changed_region].end() == cur_committed.end(),
    "end should not change");
  return result;
}

HeapWord* CardTableExtension::lowest_prev_committed_start(int ind) const {
  assert(_cur_covered_regions >= 0, "Expecting at least on region");
  HeapWord* min_start = _committed[ind].start();
  for (int j = 0; j < ind; j++) {
    HeapWord* this_start = _committed[j].start();
    //如果两个有交集
    if ((this_start < min_start) &&
        !(_committed[j].intersection(_committed[ind])).is_empty()) {
       min_start = this_start;
    }
  }
  return min_start;
}

//根据new_region的起始地址修改changed_region对应的_committed元素
void CardTableExtension::resize_update_committed_table(int changed_region,
                                                       MemRegion new_region) {

  jbyte* new_start = byte_for(new_region.start());
  // Set the new start of the committed region
  HeapWord* new_start_aligned =
    (HeapWord*)align_size_down((uintptr_t)new_start,
                             os::vm_page_size());
  MemRegion new_committed = MemRegion(new_start_aligned,
    _committed[changed_region].end());
  _committed[changed_region] = new_committed;
  _committed[changed_region].set_start(new_start_aligned);
}

void CardTableExtension::resize_update_card_table_entries(int changed_region,
                                                          MemRegion new_region) {
  debug_only(verify_guard();)
  MemRegion original_covered = _covered[changed_region];
  //new_region的基地址不能超过堆内存的基地址
  jbyte* entry;
  if (new_region.start() < _whole_heap.start()) {
    entry = byte_for(_whole_heap.start());
  } else {
    entry = byte_for(new_region.start());
  }
  jbyte* end = byte_for(original_covered.start());
  //如果扩容了则entry会小于end,将扩容内存对应卡表项置为clean_card
  while (entry < end) { *entry++ = clean_card; }
}

void CardTableExtension::resize_update_covered_table(int changed_region,
                                                     MemRegion new_region) {
  //更新_covered元素
  _covered[changed_region].set_start(new_region.start());
  _covered[changed_region].set_word_size(new_region.word_size());

  // 将所有_covered元素按照起始地址升序的方式排序
  for (int i = _cur_covered_regions-1 ; i > 0; i--) {
    if (_covered[i].start() < _covered[i-1].start()) {
        MemRegion covered_mr = _covered[i-1];
        _covered[i-1] = _covered[i];
        _covered[i] = covered_mr;
        MemRegion committed_mr = _committed[i-1];
      _committed[i-1] = _committed[i];
      _committed[i] = committed_mr;
      break;
    }
  }
}

二、G1SATBCardTableLoggingModRefBS

       G1SATBCardTableModRefBS继承自CardTableModRefBSForCTRS,CardTableModRefBSForCTRS的定义和CardTableModRefBS在同一个cardTableModRefBS.hpp中,该类的定义很简单,如下:

G1SATBCardTableModRefBS的定义在hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp中,是为了支持G1的SATB(snapshot-at-the-beginning)并发标记算法而定制的特殊BarrierSet,改写了父类CardTableModRefBS的诸多方法的实现,比如:

该方法在CardTableModRefBS中返回false,表示不支持在写入引用类型的属性时执行预处理,重点关注以下方法的改写。

1、write_ref_field_pre_work  /write_ref_array_pre

     这两方法在CardTableModRefBS中因为has_write_ref_pre_barrier方法返回false,所以都是默认采用父类BarrierSet的空实现,如下图:

 G1SATBCardTableModRefBS中的实现如下:

virtual void write_ref_field_pre_work(oop* field, oop new_val) {
    inline_write_ref_field_pre(field, new_val);
  }

virtual void write_ref_field_pre_work(narrowOop* field, oop new_val) {
    inline_write_ref_field_pre(field, new_val);
  }

template <class T> inline void inline_write_ref_field_pre(T* field, oop newVal) {
    write_ref_field_pre_static(field, newVal);
  }

template <class T> static void write_ref_field_pre_static(T* field, oop newVal) {
    T heap_oop = oopDesc::load_heap_oop(field);
    //如果oop非空
    if (!oopDesc::is_null(heap_oop)) {
      //如果采用指针压缩,即T是narrowOop时需要做decode还原
      enqueue(oopDesc::decode_heap_oop(heap_oop));
    }
  }

void G1SATBCardTableModRefBS::write_ref_array_pre(oop* dst, int count, bool dest_uninitialized) {
  if (!dest_uninitialized) {
    write_ref_array_pre_work(dst, count);
  }
}
void G1SATBCardTableModRefBS::write_ref_array_pre(narrowOop* dst, int count, bool dest_uninitialized) {
  if (!dest_uninitialized) {
    write_ref_array_pre_work(dst, count);
  }
}

template <class T> void
G1SATBCardTableModRefBS::write_ref_array_pre_work(T* dst, int count) {
  if (!JavaThread::satb_mark_queue_set().is_active()) return;
  T* elem_ptr = dst;
  //遍历dst之后count个oop
  for (int i = 0; i < count; i++, elem_ptr++) {
    T heap_oop = oopDesc::load_heap_oop(elem_ptr);
    if (!oopDesc::is_null(heap_oop)) {
      enqueue(oopDesc::decode_heap_oop_not_null(heap_oop));
    }
  }
}


void G1SATBCardTableModRefBS::enqueue(oop pre_val) {
  //必须是oop
  assert(pre_val->is_oop(true), "Error");
  //如果satb quene不是活跃的则返回
  if (!JavaThread::satb_mark_queue_set().is_active()) return;
  Thread* thr = Thread::current();
  //如果是Java线程
  if (thr->is_Java_thread()) {
    JavaThread* jt = (JavaThread*)thr;
    //加入到队列中
    jt->satb_mark_queue().enqueue(pre_val);
  } else {
    //如果是JVM线程
    MutexLockerEx x(Shared_SATB_Q_lock, Mutex::_no_safepoint_check_flag);
    JavaThread::satb_mark_queue_set().shared_satb_queue()->enqueue(pre_val);
  }
}

上述方法实现的核心最终都落脚到enqueue方法,落脚到ObjPtrQueue和SATBMarkQueueSet两个类的实现,下一篇博客会详细探讨这两个类的实现。

2、set_card_claimed /g1_mark_as_young /mark_card_deferred

     CardTableModRefBS中只用到了dirty_card和clean_card两种CardValue,G1SATBCardTableModRefBS增加了claimed_card和deferred_card两种,并自定义了一个扩展的g1_young_gen,表示该卡表项对应的内存区域是一个young gen,其定义如下:

其中claimed_card可以和其他的非clean_card共存,除dirty_card外;deferred_card是和claimed_card配合使用,只能从clean_card或者claimed_card转换成deferred_card。上面三个方法就是将对应的卡表项设置为对应的CardValue,其实现如下:

void set_card_claimed(size_t card_index) {
      //获取对应卡表项的值
      jbyte val = _byte_map[card_index];
      if (val == clean_card_val()) {
        val = (jbyte)claimed_card_val();
      } else {
        //使用或运算,会保留卡表项原来的状态
        val |= (jbyte)claimed_card_val();
      }
      _byte_map[card_index] = val;
  }

void G1SATBCardTableModRefBS::g1_mark_as_young(const MemRegion& mr) {
  //计算mr对应的起始卡表项
  jbyte *const first = byte_for(mr.start());
  jbyte *const last = byte_after(mr.last());

  // UseMemSetInBOT默认为true,表示是否在GC的代码中使用memset完成内存值设置
  if (UseMemSetInBOT) {
    memset(first, g1_young_gen, last - first);
  } else {
    //通过指针的方式遍历
    for (jbyte* i = first; i < last; i++) {
      *i = g1_young_gen;
    }
  }
}

bool G1SATBCardTableModRefBS::mark_card_deferred(size_t card_index) {
  //获取对应卡表项的值
  jbyte val = _byte_map[card_index];
  //如果已经是deferred_card
  if ((val & (clean_card_mask_val() | deferred_card_val())) == deferred_card_val()) {
    return false;
  }

  if  (val == g1_young_gen) {
    //不需要跟踪指向年轻代的指针
    return false;
  }

  // Cached bit can be installed either on a clean card or on a claimed card.
  jbyte new_val = val;
  if (val == clean_card_val()) {
    new_val = (jbyte)deferred_card_val();
  } else {
    //该卡表项必须原来是claimed_card
    if (val & claimed_card_val()) {
      new_val = val | (jbyte)deferred_card_val();
    }
  }
  if (new_val != val) {
    //原子的更新状态,更新失败不影响
    Atomic::cmpxchg(new_val, &_byte_map[card_index], val);
  }
  return true;
}

static int claimed_card_val()    { return claimed_card; }

static int clean_card_val()      { return clean_card; }

static int deferred_card_val()   { return deferred_card; }

三、G1SATBCardTableLoggingModRefBS

      G1SATBCardTableLoggingModRefBS跟G1SATBCardTableLoggingModRefBS定义在同一个g1SATBCardTableModRefBS.hpp中,G1实际使用的是G1SATBCardTableLoggingModRefBS作为BarrierSet的实现类,该类同样对父类的实现做了大幅调整,以适配G1的垃圾回收机制,重点关注以下方法的改写。

1、构造方法和initialize

     G1SATBCardTableLoggingModRefBS新增了两个属性,如下:

这两个都是在构造方法中完成初始化,如下:

 

 G1SATBCardTableLoggingModRefBS将父类initialize的实现改成了空实现,增加了一个有参数的initialize方法完成其初始化,如下:

该方法的实现源码如下:

void G1SATBCardTableLoggingModRefBS::initialize(G1RegionToSpaceMapper* mapper) {
  mapper->set_mapping_changed_listener(&_listener);
  //G1RegionToSpaceMapper相当于整个卡表了,没有走父类的申请内存逻辑
  _byte_map_size = mapper->reserved().byte_size();

  _guard_index = cards_required(_whole_heap.word_size()) - 1;
  _last_valid_index = _guard_index - 1;

  HeapWord* low_bound  = _whole_heap.start();
  HeapWord* high_bound = _whole_heap.end();
  
  //只有一个covered元素,并且元素就是整个Java堆
  _cur_covered_regions = 1;
  _covered[0] = _whole_heap;

  _byte_map = (jbyte*) mapper->reserved().start();
  byte_map_base = _byte_map - (uintptr_t(low_bound) >> card_shift);
  assert(byte_for(low_bound) == &_byte_map[0], "Checking start of map");
  assert(byte_for(high_bound-1) <= &_byte_map[_last_valid_index], "Checking end of map");

  if (TraceCardTableModRefBS) {
    //打印日志
    gclog_or_tty->print_cr("G1SATBCardTableModRefBS::G1SATBCardTableModRefBS: ");
    gclog_or_tty->print_cr("  "
                  "  &_byte_map[0]: " INTPTR_FORMAT
                  "  &_byte_map[_last_valid_index]: " INTPTR_FORMAT,
                  p2i(&_byte_map[0]),
                  p2i(&_byte_map[_last_valid_index]));
    gclog_or_tty->print_cr("  "
                  "  byte_map_base: " INTPTR_FORMAT,
                  p2i(byte_map_base));
  }
}

 该方法相比父类的实现最大的区别有两点,一是没有通过ReservedSpace申请内存,传入的参数G1RegionToSpaceMapper相当于一个已经申请好内存的卡表;二是没有初始化_committed数组,coverd元素只有一个,就是_whole_heap。

该方法的调用链如下:

构造方法的调用链如下:

 

2、G1SATBCardTableLoggingModRefBSChangedListener

     G1SATBCardTableLoggingModRefBSChangedListener的定义和G1SATBCardTableLoggingModRefBS在同一个文件中,表示G1下CardTable内存变化后触发的动作,该类的定义如下:

G1MappingChangedListener就是一个定义了on_commit方法的接口类,该类的定义如下:

 

 G1SATBCardTableLoggingModRefBSChangedListener的on_commit方法的实现如下:

即将start_idx对应的内存区域对应的卡表项初始化为clean_card。

3、write_ref_field_work /write_region_work /write_ref_array_work /invalidate

     上述方法在父类中的实现都是将对应的卡表项置为dirty_card而已,G1SATBCardTableLoggingModRefBS的实现多了一步,将这些dirty_card的卡表项放入DirtyCardQueue中管理起来,避免卡表的扫描。上述方法的实现如下:

void
G1SATBCardTableLoggingModRefBS::write_ref_field_work(void* field,
                                                     oop new_val,
                                                     bool release) {
  //获取对应的卡表项                                                   
  volatile jbyte* byte = byte_for(field);
  //不处理g1_young_gen的卡表项
  if (*byte == g1_young_gen) {
    return;
  }
  OrderAccess::storeload();
  if (*byte != dirty_card) {
    //将卡表项置为dirty_card
    *byte = dirty_card;
    Thread* thr = Thread::current();
    //将该卡表项放入DirtyCardQueue中
    if (thr->is_Java_thread()) {
      JavaThread* jt = (JavaThread*)thr;
      jt->dirty_card_queue().enqueue(byte);
    } else {
      MutexLockerEx x(Shared_DirtyCardQ_lock,
                      Mutex::_no_safepoint_check_flag);
      _dcqs.shared_dirty_card_queue()->enqueue(byte);
    }
  }
}

void write_region_work(MemRegion mr)    { invalidate(mr); }

void write_ref_array_work(MemRegion mr) { invalidate(mr); }

void
G1SATBCardTableLoggingModRefBS::invalidate(MemRegion mr, bool whole_heap) {
  //获取mr内存区域对应的起止卡表项
  volatile jbyte* byte = byte_for(mr.start());
  jbyte* last_byte = byte_for(mr.last());
  Thread* thr = Thread::current();
  if (whole_heap) {
    //如果是整个堆内存
    while (byte <= last_byte) {
      *byte = dirty_card;
      byte++;
    }
  } else {
    //跳过连续的表示young gen的卡表项
    for (; byte <= last_byte && *byte == g1_young_gen; byte++);

    if (byte <= last_byte) {
      OrderAccess::storeload();
      // Enqueue if necessary.
      if (thr->is_Java_thread()) {
        JavaThread* jt = (JavaThread*)thr;
        for (; byte <= last_byte; byte++) {
          //过滤g1_young_gen
          if (*byte == g1_young_gen) {
            continue;
          }
          if (*byte != dirty_card) {
            //置为dirty_card,并放入队列中
            *byte = dirty_card;
            jt->dirty_card_queue().enqueue(byte);
          }
        }
      } else {
        //非Java线程需要获取锁Shared_DirtyCardQ_lock
        MutexLockerEx x(Shared_DirtyCardQ_lock,
                        Mutex::_no_safepoint_check_flag);
        for (; byte <= last_byte; byte++) {
          ////过滤g1_young_gen
          if (*byte == g1_young_gen) {
            continue;
          }
          if (*byte != dirty_card) {
            *byte = dirty_card;
            _dcqs.shared_dirty_card_queue()->enqueue(byte);
          }
        }
      }
    }
  }
}

4、resize_covered_region

     G1SATBCardTableLoggingModRefBS下resize_covered_region不会被调用,因为covered元素只有一个,就是表示整个堆内存的whole_heap,所以将该方法改成一个空实现,如下图:

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!