美文网首页
DefNewGeneration 之二

DefNewGeneration 之二

作者: 程序员札记 | 来源:发表于2022-11-21 20:53 被阅读0次

1、gc_prologue / gc_epilogue
gc_prologue方法是在GC开始前调用的预处理,gc_epilogue是在GC结束后调用的尾处理,其实现如下:

void DefNewGeneration::gc_prologue(bool full) {
  // Ensure that _end and _soft_end are the same in eden space.
  eden()->set_soft_end(eden()->end());
}
 
void DefNewGeneration::gc_epilogue(bool full) {
  DEBUG_ONLY(static bool seen_incremental_collection_failed = false;)
 
  assert(!GC_locker::is_active(), "We should not be executing here");
  GenCollectedHeap* gch = GenCollectedHeap::heap();
  if (full) {
    DEBUG_ONLY(seen_incremental_collection_failed = false;)
    //正常情况下GC结束后eden区是空的,如果非空说明堆内存满了,eden区中的存活对象未拷贝至老年代中
    if (!collection_attempt_is_safe() && !_eden_space->is_empty()) {
      if (Verbose && PrintGCDetails) {
        gclog_or_tty->print("DefNewEpilogue: cause(%s), full, not safe, set_failed, set_alloc_from, clear_seen",
                            GCCause::to_string(gch->gc_cause()));
      }
      //通知GCH promote事变
      gch->set_incremental_collection_failed(); // Slight lie: a full gc left us in that state
      //允许使用from区分配对象
      set_should_allocate_from_space(); // we seem to be running out of space
    } else {
      if (Verbose && PrintGCDetails) {
        gclog_or_tty->print("DefNewEpilogue: cause(%s), full, safe, clear_failed, clear_alloc_from, clear_seen",
                            GCCause::to_string(gch->gc_cause()));
      }
      gch->clear_incremental_collection_failed(); // We just did a full collection
      clear_should_allocate_from_space(); // if set
    }
  } else {
 
  }
 
  if (ZapUnusedHeapArea) {
    //mangle三个区
    eden()->check_mangled_unused_area_complete();
    from()->check_mangled_unused_area_complete();
    to()->check_mangled_unused_area_complete();
  }
 
  if (!CleanChunkPoolAsync) {
    //清空ChunkPool
    Chunk::clean_chunk_pool();
  }
 
  //更新计数器
  update_counters();
  gch->collector_policy()->counters()->update_counters();
}
 
//该方法返回尝试回收垃圾是否是安全的
bool DefNewGeneration::collection_attempt_is_safe() {
  if (!to()->is_empty()) {
    if (Verbose && PrintGCDetails) {
      gclog_or_tty->print(" :: to is not empty :: ");
    }
    //如果to区非空则返回false,正常都是空的
    return false;
  }
  if (_next_gen == NULL) {
    //初始化_next_gen,DefNewGeneration第一次准备垃圾回收前_next_gen一直为null
    GenCollectedHeap* gch = GenCollectedHeap::heap();
    _next_gen = gch->next_gen(this);
  }
  //判断next_gen是否有充足的空间,允许年轻代的对象复制到老年代中
  return _next_gen->promotion_attempt_is_safe(used());
}

两者的调用链如下:


image.png image.png

2、 compute_new_size
compute_new_size用于在GC结束后根据老年代的大小和NewRatio,NewSizeThreadIncrease两个参数重新计算年轻代的大小,并做适当的扩容或者缩容处理。

void DefNewGeneration::compute_new_size() {
  //正常from区或者to区在GC结束后都是空的,如果非空则说明堆内存已满
  if (!from()->is_empty() || !to()->is_empty()) {
    return;
  }
 
  int next_level = level() + 1;
  GenCollectedHeap* gch = GenCollectedHeap::heap();
  assert(next_level < gch->_n_gens,
         "DefNewGeneration cannot be an oldest gen");
 
  Generation* next_gen = gch->_gens[next_level];
  size_t old_size = next_gen->capacity();
  size_t new_size_before = _virtual_space.committed_size();
  size_t min_new_size = spec()->init_size();
  size_t max_new_size = reserved().byte_size();
  assert(min_new_size <= new_size_before &&
         new_size_before <= max_new_size,
         "just checking");
  size_t alignment = Generation::GenGrain;
 
  // 计算年轻代新的大小,基于NewRatio和NewSizeThreadIncrease
  size_t desired_new_size = old_size/NewRatio;
  //获取非后台线程数
  int threads_count = Threads::number_of_non_daemon_threads();
  //NewSizeThreadIncrease表示每个非后台线程增加的年轻代的内存大小,默认是4k
  size_t thread_increase_size = threads_count * NewSizeThreadIncrease;
  desired_new_size = align_size_up(desired_new_size + thread_increase_size, alignment);
 
  // Adjust new generation size
  desired_new_size = MAX2(MIN2(desired_new_size, max_new_size), min_new_size);
  assert(desired_new_size <= max_new_size, "just checking");
 
  bool changed = false;
  if (desired_new_size > new_size_before) {
    //如果大于原来的,则需要扩容
    size_t change = desired_new_size - new_size_before;
    assert(change % alignment == 0, "just checking");
    //尝试扩容指定大小的内存
    if (expand(change)) {
       //扩容成功,置为true
       changed = true;
    }
  }
  if (desired_new_size < new_size_before && eden()->is_empty()) {
    //则eden区是空的情形下才允许缩容,非空的条件下缩容有可能导致对象丢失
    size_t change = new_size_before - desired_new_size;
    assert(change % alignment == 0, "just checking");
    _virtual_space.shrink_by(change);
    changed = true;
  }
  if (changed) {
    //如果改变,则重新计算三个区的内存边界并初始化,compute_space_boundaries方法会保证eden区的内存足够大
    compute_space_boundaries(eden()->used(),
                             SpaceDecorator::Clear,
                             SpaceDecorator::DontMangle);
    MemRegion cmr((HeapWord*)_virtual_space.low(),
                  (HeapWord*)_virtual_space.high());
    //重置bs对应的覆盖区域              
    Universe::heap()->barrier_set()->resize_covered_region(cmr);
    if (Verbose && PrintGC) {
      size_t new_size_after  = _virtual_space.committed_size();
      size_t eden_size_after = eden()->capacity();
      size_t survivor_size_after = from()->capacity();
      gclog_or_tty->print("New generation size " SIZE_FORMAT "K->"
        SIZE_FORMAT "K [eden="
        SIZE_FORMAT "K,survivor=" SIZE_FORMAT "K]",
        new_size_before/K, new_size_after/K,
        eden_size_after/K, survivor_size_after/K);
      if (WizardMode) {
        gclog_or_tty->print("[allowed " SIZE_FORMAT "K extra for %d threads]",
          thread_increase_size/K, threads_count);
      }
      gclog_or_tty->cr();
    }
  }
}
 
GenerationSpec* Generation::spec() {
  GenCollectedHeap* gch = GenCollectedHeap::heap();
  assert(0 <= level() && level() < gch->_n_gens, "Bad gen level");
  return gch->_gen_specs[level()];
}
 
MemRegion reserved() const { return _reserved; }
 
bool DefNewGeneration::expand(size_t bytes) {
  MutexLocker x(ExpandHeap_lock);
  HeapWord* prev_high = (HeapWord*) _virtual_space.high();
  //尝试扩容指定大小的内存
  bool success = _virtual_space.expand_by(bytes);
  if (success && ZapUnusedHeapArea) {
    //扩容成功,执行mangle操作
    HeapWord* new_high = (HeapWord*) _virtual_space.high();
    MemRegion mangle_region(prev_high, new_high);
    SpaceMangler::mangle_region(mangle_region);
  }
 
  if (GC_locker::is_active()) {
    if (PrintGC && Verbose) {
      gclog_or_tty->print_cr("Garbage collection disabled, "
        "expanded heap instead");
    }
  }
 
  return success;
}

其调用链如下:

image.png

3、copy_to_survivor_space
copy_to_survivor_space会将对象拷贝到to区或者老年代,如果对象的分代年龄大于tenuring_threshold或者从to区申请内存失败则拷贝到老年代,否则拷贝到to区;如果拷贝到老年代失败,则_promotion_failed置为true,并将该对象保存到_promo_failure_scan_stack栈中。其实现如下:

oop DefNewGeneration::copy_to_survivor_space(oop old) {
  assert(is_in_reserved(old) && !old->is_forwarded(),
         "shouldn't be scavenging this oop");
  size_t s = old->size();
  oop obj = NULL;
 
  // Try allocating obj in to-space (unless too old)
  if (old->age() < tenuring_threshold()) {
    //如果对象的年龄低于tenuring_threshold,则该在to区申请一块同样大小的内存
    obj = (oop) to()->allocate_aligned(s);
  }
 
  // Otherwise try allocating obj tenured
  if (obj == NULL) {
    //如果如果对象的年龄大于tenuring_threshold或者to区申请内存失败
    //则尝试将该对象复制到老年代
    obj = _next_gen->promote(old, s);
    if (obj == NULL) {
      //复制失败
      handle_promotion_failure(old);
      return old;
    }
  } else {
    //to区中申请内存成功
    const intx interval = PrefetchCopyIntervalInBytes;
    Prefetch::write(obj, interval);
 
    //对象复制
    Copy::aligned_disjoint_words((HeapWord*)old, (HeapWord*)obj, s);
 
    //增加年龄,并修改age_table,增加对应年龄的总对象大小
    //注意此处是增加复制对象而非原来对象的分代年龄
    obj->incr_age();
    age_table()->add(obj, s);
  }
 
  //将对象头指针指向新地址
  old->forward_to(obj);
 
  return obj;
}
 
//从请求头中获取对象的年龄
inline uint oopDesc::age() const {
  assert(!is_forwarded(), "Attempt to read age from forwarded mark");
  if (has_displaced_mark()) {
    return displaced_mark()->age();
  } else {
    return mark()->age();
  }
}
 
uint tenuring_threshold() { return _tenuring_threshold; }
 
void DefNewGeneration::handle_promotion_failure(oop old) {
  if (PrintPromotionFailure && !_promotion_failed) {
    gclog_or_tty->print(" (promotion failure size = " SIZE_FORMAT ") ",
                        old->size());
  }
  _promotion_failed = true;
  _promotion_failed_info.register_copy_failure(old->size());
  preserve_mark_if_necessary(old, old->mark());
  //将old的对象头指针指向它自己
  old->forward_to(old);
  //保存promotion失败的对象
  _promo_failure_scan_stack.push(old);
 
  //_promo_failure_drain_in_progress初始化为false
  if (!_promo_failure_drain_in_progress) {
    // prevent recursion in copy_to_survivor_space()
    _promo_failure_drain_in_progress = true;
    drain_promo_failure_scan_stack();
    _promo_failure_drain_in_progress = false;
  }
}
 
void DefNewGeneration::preserve_mark_if_necessary(oop obj, markOop m) {
  //是否要保留promotion失败的对象,对象头中包含锁,分代年龄等非初始状态的信息时需要单独保留对象头,否则无法恢复成原来的状态
  if (m->must_be_preserved_for_promotion_failure(obj)) {
    preserve_mark(obj, m);
  }
}
 
//保留指定的对象和对象头
void DefNewGeneration::preserve_mark(oop obj, markOop m) {
  assert(_promotion_failed && m->must_be_preserved_for_promotion_failure(obj),
         "Oversaving!");
  _objs_with_preserved_marks.push(obj);
  _preserved_marks_of_objs.push(m);
}
 
//用于移除并处理_promo_failure_scan_stack中保存的对象
void DefNewGeneration::drain_promo_failure_scan_stack() {
  while (!_promo_failure_scan_stack.is_empty()) {
     oop obj = _promo_failure_scan_stack.pop();
     //注意promote失败并不会终止根节点遍历,相反还会遍历其引用类型属性
     obj->oop_iterate(_promo_failure_scan_stack_closure);
  }
}

其调用链如下:

image.png

4、 ageTable
ageTable的定义在hotspot\src\share\vm\gc_implementation\shared\ageTable.hpp中,用来记录不同分代年龄的对象的大小,然后据此动态调整tenuring_threshold,重要属性只有一个,如下:

image.png

sizes就是保存不同分代年龄的对象的大小的数组。重点关注以下方法的实现,clear方法用于将sizes数组中各元素的值置为0,add方法用于增加某个分代年龄下的对象大小,compute_tenuring_threshold方法用于计算新的tenuring_threshold,保证to区中已使用空间占总空间的比例满足TargetSurvivorRatio的要求,如下:

ageTable::ageTable(bool global) {
 
  clear();
 
  if (UsePerfData && global) {
 
    ResourceMark rm;
    EXCEPTION_MARK;
 
    const char* agetable_ns = "generation.0.agetable";
    const char* bytes_ns = PerfDataManager::name_space(agetable_ns, "bytes");
    
    //初始化用于收集性能的PerfVariable
    for(int age = 0; age < table_size; age ++) {
      char age_name[10];
      jio_snprintf(age_name, sizeof(age_name), "%2.2d", age);
      const char* cname = PerfDataManager::counter_name(bytes_ns, age_name);
      _perf_sizes[age] = PerfDataManager::create_variable(SUN_GC, cname,
                                                          PerfData::U_Bytes,
                                                          CHECK);
    }
 
    const char* cname = PerfDataManager::counter_name(agetable_ns, "size");
    PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_None,
                                     table_size, CHECK);
  }
}
 
void ageTable::clear() {
  //将所有的数组元素的值重置为0
  for (size_t* p = sizes; p < sizes + table_size; ++p) {
    *p = 0;
  }
}
 
void add(oop p, size_t oop_size) {
    add(p->age(), oop_size);
  }
 
void add(uint age, size_t oop_size) {
    assert(age > 0 && age < table_size, "invalid age of object");
    sizes[age] += oop_size;
  }
 
uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {
  //TargetSurvivorRatio表示GC后Survivor区已使用内存占总内存的比例,默认是50
  //计算期望的survivor区的大小
  size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);
  size_t total = 0;
  uint age = 1;
  assert(sizes[0] == 0, "no objects with age zero should be recorded");
  //从低到高依次遍历ageTable,将给分代年龄对应的对象大小加起来,一旦超过desired_survivor_size,则该age就是目标age
  //低于该age的对象就会被拷贝到to区,从而保证Survivor区的内存在GC结束后满足TargetSurvivorRatio限制
  while (age < table_size) {
    total += sizes[age];
    // check if including objects of age 'age' made us pass the desired
    // size, if so 'age' is the new threshold
    if (total > desired_survivor_size) break;
    age++;
  }
  uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;
 
  if (PrintTenuringDistribution || UsePerfData) {
    //打印日志,更新PerfData和相关计数器
    if (PrintTenuringDistribution) {
      gclog_or_tty->cr();
      gclog_or_tty->print_cr("Desired survivor size " SIZE_FORMAT " bytes, new threshold %u (max %u)",
        desired_survivor_size*oopSize, result, (int) MaxTenuringThreshold);
    }
 
    total = 0;
    age = 1;
    while (age < table_size) {
      total += sizes[age];
      if (sizes[age] > 0) {
        if (PrintTenuringDistribution) {
          gclog_or_tty->print_cr("- age %3u: " SIZE_FORMAT_W(10) " bytes, " SIZE_FORMAT_W(10) " total",
                                        age,    sizes[age]*oopSize,          total*oopSize);
        }
      }
      if (UsePerfData) {
        _perf_sizes[age]->set_value(sizes[age]*oopSize);
      }
      age++;
    }
    if (UsePerfData) {
      SharedHeap* sh = SharedHeap::heap();
      CollectorPolicy* policy = sh->collector_policy();
      GCPolicyCounters* gc_counters = policy->counters();
      gc_counters->tenuring_threshold()->set_value(result);
      gc_counters->desired_survivor_size()->set_value(
        desired_survivor_size*oopSize);
    }
  }
 
  return result;
}

5、IsAliveClosure / ScanWeakRefClosure / FastKeepAliveClosure
IsAliveClosure用来判断某个oop是否是存活的;ScanWeakRefClosure用于扫描弱引用的;FastKeepAliveClosure继承自KeepAliveClosure,只适用于DefNewGeneration,用于将某个oop标记成存活状态,具体来说就是将该对象拷贝到to区或者老年代,然后更新对象的对象头指针和BS对应的卡表项,从而让is_forwarded方法返回true;上述三个类,除ScanWeakRefClosure定义在hotspot\src\share\vm\memory\genOopClosures.hpp中,另外两个都在同目录的defNewGeneration.hpp中,其实现如下:

//IsAliveClosure只适用于年轻代
DefNewGeneration::IsAliveClosure::IsAliveClosure(Generation* g) : _g(g) {
  assert(g->level() == 0, "Optimized for youngest gen.");
}
bool DefNewGeneration::IsAliveClosure::do_object_b(oop p) {
  //如果p不在年轻代或者p即将被复制则认为p是存活的
  return (HeapWord*)p >= _g->reserved().end() || p->is_forwarded();
}
 
inline bool oopDesc::is_forwarded() const {
  return mark()->is_marked();
}
 
bool is_marked()   const {
    return (mask_bits(value(), lock_mask_in_place) == marked_value);
}
 
 
 
ScanWeakRefClosure::ScanWeakRefClosure(DefNewGeneration* g) :
  _g(g)
{
  assert(_g->level() == 0, "Optimized for youngest generation");
  _boundary = _g->reserved().end();
}
 
template <class T> inline void ScanWeakRefClosure::do_oop_work(T* p) {
  //校验oop非空
  assert(!oopDesc::is_null(*p), "null weak reference?");
  //获取oop
  oop obj = oopDesc::load_decode_heap_oop_not_null(p);
  //校验obj在年轻代中且不在to区中
  if ((HeapWord*)obj < _boundary && !_g->to()->is_in_reserved(obj)) {
    //获取obj的拷贝地址,如果该对象因为Space压缩已经有拷贝地址则使用该地址,否则将该对象拷贝到to区或者老年代
    //copy_to_survivor_space方法会完成拷贝并返回拷贝到to区或者老年代的新地址,如果拷贝失败返回NULL
    oop new_obj = obj->is_forwarded() ? obj->forwardee()
                                      : _g->copy_to_survivor_space(obj);
    //将p指向新地址                                  
    oopDesc::encode_store_heap_oop_not_null(p, new_obj);
  }
}
 
DefNewGeneration::KeepAliveClosure::
KeepAliveClosure(ScanWeakRefClosure* cl) : _cl(cl) {
  GenRemSet* rs = GenCollectedHeap::heap()->rem_set();
  assert(rs->rs_kind() == GenRemSet::CardTable, "Wrong rem set kind.");
  _rs = (CardTableRS*)rs;
}
 
template <class T>
inline void DefNewGeneration::KeepAliveClosure::do_oop_work(T* p) {
 
  _cl->do_oop_nv(p);
 
  if (Universe::heap()->is_in_reserved(p)) {
    oop obj = oopDesc::load_decode_heap_oop_not_null(p);
    //将对应的卡表项设置为youngergen_card,而非脏的
    _rs->inline_write_ref_field_gc(p, obj);
  }
 
//FastKeepAliveClosure只适用于DefNewGeneration
DefNewGeneration::FastKeepAliveClosure::
FastKeepAliveClosure(DefNewGeneration* g, ScanWeakRefClosure* cl) :
  DefNewGeneration::KeepAliveClosure(cl) {
  _boundary = g->reserved().end();
}
 
template <class T>
inline void DefNewGeneration::FastKeepAliveClosure::do_oop_work(T* p) {
 
  _cl->do_oop_nv(p);
 
  oop obj = oopDesc::load_decode_heap_oop_not_null(p);
  //同KeepAliveClosure的实现,多了一个地址范围的判断
  if (((HeapWord*)obj < _boundary) && Universe::heap()->is_in_reserved(p)) {
    _rs->inline_write_ref_field_gc(p, obj);
  }
}

与is_marked对应的set_marked方法的调用链如下:

image.png

6、FastScanClosure / KlassScanClosure / CLDToKlassAndOopClosure / FastEvacuateFollowersClosure
FastScanClosure用于遍历年轻代中的存活对象oop,如果该对象没有promote,则执行promote,根据对象的分代年龄将对象拷贝到to区或者老年代,并将拷贝的目标地址写入对象头中;然后修改oop使其指向对象的新地址,即对象头中的forward指针;最后如果_scanned_klass不为空则标记该klass的oop发生了修改,如果_gc_barrier为true,则会将对象地址对应的卡表项置为youngergen_card。其实现如下:

FastScanClosure::FastScanClosure(DefNewGeneration* g, bool gc_barrier) :
    OopsInKlassOrGenClosure(g), _g(g), _gc_barrier(gc_barrier)
{
  assert(_g->level() == 0, "Optimized for youngest generation");
  //初始化成年轻代的终止地址
  _boundary = _g->reserved().end();
}
 
OopsInKlassOrGenClosure(Generation* g) : OopsInGenClosure(g), _scanned_klass(NULL) {}
 
inline OopsInGenClosure::OopsInGenClosure(Generation* gen) :
  ExtendedOopClosure(gen->ref_processor()), _orig_gen(gen), _rs(NULL) {
  set_generation(gen);
}
 
inline void OopsInGenClosure::set_generation(Generation* gen) {
  _gen = gen;
  _gen_boundary = _gen->reserved().start();
  if (_rs == NULL) {
    GenRemSet* rs = SharedHeap::heap()->rem_set();
    assert(rs->rs_kind() == GenRemSet::CardTable, "Wrong rem set kind");
    _rs = (CardTableRS*)rs;
  }
}
 
 
template <class T> inline void FastScanClosure::do_oop_work(T* p) {
  T heap_oop = oopDesc::load_heap_oop(p);
  if (!oopDesc::is_null(heap_oop)) {
    //加载目标oop
    oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
    if ((HeapWord*)obj < _boundary) {
      //如果obj是年轻代的对象
      assert(!_g->to()->is_in_reserved(obj), "Scanning field twice?");
      //is_forwarded为true说明该对象已经执行被promote了
      //如果为false,则执行promote,即拷贝到to区或者老年代
      //new_obj就是执行promote的对象新地址
      oop new_obj = obj->is_forwarded() ? obj->forwardee()
                                        : _g->copy_to_survivor_space(obj);
      //让p指向对象的新地址
      oopDesc::encode_store_heap_oop_not_null(p, new_obj);
      if (is_scanning_a_klass()) {
        do_klass_barrier();
      } else if (_gc_barrier) {
        //初始化时_gen_boundary指向年轻代的起始地址,执行GenCollectedHeap::gen_process_roots时会将其置为老年代的起始地址,此时do_barrier会将p对应的卡表项置为youngergen_card
        do_barrier(p);
      }
    }
  }
}
 
//KlassScanClosure会调用set_scanned_klass来设置
bool is_scanning_a_klass() { return _scanned_klass != NULL; }
 
inline void OopsInKlassOrGenClosure::do_klass_barrier() {
  assert(_scanned_klass != NULL, "Must be");
  _scanned_klass->record_modified_oops();
}
 
//因为Klass在元空间中不能使用卡表或者BitMap来记录其引用的oop发生修改的情形,所以借助此字段记录
void record_modified_oops()            { _modified_oops = 1; }
 
template <class T> inline void OopsInGenClosure::do_barrier(T* p) {
  assert(generation()->is_in_reserved(p), "expected ref in generation");
  //获取p指向的对象地址
  T heap_oop = oopDesc::load_heap_oop(p);
  assert(!oopDesc::is_null(heap_oop), "expected non-null oop");
  //获取heap_oop的完整地址
  oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
  //_gen_boundary是当前generation的起始地址,如果obj小于gen_boundary,说明obj是更年轻的分代中的
  //如果generation本身就是年轻代,则if不成立,如果是老年代,则要求obj是年轻代的
  if ((HeapWord*)obj < _gen_boundary) {
    //将对应的卡表项标记为youngergen_card
    _rs->inline_write_ref_field_gc(p, obj);
  }
}

KlassScanClosure与FastScanClosure配合使用,用来遍历所引用的oop发生修改的Klass对应的类Class实例,遍历逻辑就是FastScanClosure。其实现如下:

KlassScanClosure::KlassScanClosure(OopsInKlassOrGenClosure* scavenge_closure,
                                   KlassRemSet* klass_rem_set)
    : _scavenge_closure(scavenge_closure),
     //accumulate_modified_oops属性默认为false,老年代GC时会设置该属性
      _accumulate_modified_oops(klass_rem_set->accumulate_modified_oops()) {}
 
void KlassScanClosure::do_klass(Klass* klass) {
 
  // If the klass has not been dirtied we know that there's
  // no references into  the young gen and we can skip it.
  if (klass->has_modified_oops()) {
    if (_accumulate_modified_oops) {
      //将_accumulated_modified_oops置为1
      klass->accumulate_modified_oops();
    }
 
    //将_modified_oops恢复成0
    klass->clear_modified_oops();
 
    //通知_scavenge_closure准备扫描klass
    _scavenge_closure->set_scanned_klass(klass);
 
    //执行的过程中如果该Klass对应的java_mirror还在年轻代则将该klass的_modified_oops再次置为1
    //这样做的目的是确保下一次年轻代GC时还会将该对象作为存活对象处理,直到将其promote到老年代为止
    klass->oops_do(_scavenge_closure);
 
    _scavenge_closure->set_scanned_klass(NULL);
  }
}
 
bool has_modified_oops()               { return _modified_oops == 1; }
 
void accumulate_modified_oops()        { 
   if (has_modified_oops()) _accumulated_modified_oops = 1; 
}
 
void clear_modified_oops()             { _modified_oops = 0; }
 
void Klass::oops_do(OopClosure* cl) {
  cl->do_oop(&_java_mirror);
}

与has_modified_oops方法对应的record_modified_oops方法的调用链如下:

image.png

即主要是在创建Klass的过程中设置java_mirror属性时调用的,该属性也是klass中的唯一一个oop, 即FastScanClosure主要遍历在上一次GC与本次GC之间新加载或者创建的Klass对应的java_mirror。

CLDToKlassAndOopClosure与FastScanClosure,KlassScanClosure配合使用,用来遍历一个ClassLoader实例加载的所有类的Class实例及其关联的依赖,常量池引用等。

CLDToKlassAndOopClosure(KlassClosure* klass_closure,
                          OopClosure* oop_closure,
                          bool must_claim_cld) : //DefNewGeneration调用时传入false,因为是单线程GC的,多线程GC时通常传入true
                              _oop_closure(oop_closure),
                              _klass_closure(klass_closure),
                              _must_claim_cld(must_claim_cld) {}
 
void CLDToKlassAndOopClosure::do_cld(ClassLoaderData* cld) {
  cld->oops_do(_oop_closure, _klass_closure, _must_claim_cld);
}
 
void ClassLoaderData::oops_do(OopClosure* f, KlassClosure* klass_closure, bool must_claim) {
  //claim方法用于判断当前ClassLoaderData是否遍历过
  if (must_claim && !claim()) {
    return;
  }
 
  f->do_oop(&_class_loader);
  _dependencies.oops_do(f);
  _handles.oops_do(f);
  if (klass_closure != NULL) {
    //遍历该ClassLoaderData加载的所有Klass
    classes_do(klass_closure);
  }
}
 
void ClassLoaderData::classes_do(KlassClosure* klass_closure) {
  for (Klass* k = _klasses; k != NULL; k = k->next_link()) {
    klass_closure->do_klass(k);
    assert(k != k->next_link(), "no loops!");
  }
}

FastEvacuateFollowersClosure比较特殊,主要用来遍历所有被promote到老年代的对象,恢复他们的对象头,并且遍历其包含的所有引用类型属性,其实现如下:

DefNewGeneration::FastEvacuateFollowersClosure::
FastEvacuateFollowersClosure(GenCollectedHeap* gch, int level,
                             DefNewGeneration* gen,
                             FastScanClosure* cur, FastScanClosure* older) :
  _gch(gch), _level(level), _gen(gen),
  _scan_cur_or_nonheap(cur), _scan_older(older)
{}
 
void DefNewGeneration::FastEvacuateFollowersClosure::do_void() {
  do {
    //_level是年轻代的level
    //单线程GC时,年轻代的saved_mark_word就是top,所以oop_since_save_marks_iterate不做任何护理
   //此方法主要是遍历被promote到老年代的对象,因为是单线程执行,所以执行一遍后老年代的no_allocs_since_save_marks方法就会返回true,从而终止遍历
    _gch->oop_since_save_marks_iterate(_level, _scan_cur_or_nonheap,
                                       _scan_older);
  } while (!_gch->no_allocs_since_save_marks(_level));
  guarantee(_gen->promo_failure_scan_is_complete(), "Failed to finish scan");
}
 
bool GenCollectedHeap::no_allocs_since_save_marks(int level) {
  //如果level为0,则处理年轻代和老年代,如果level为1则只处理老年代 
  for (int i = level; i < _n_gens; i++) {
    if (!_gens[i]->no_allocs_since_save_marks()) return false;
  }
  return true;
}
 
//执行do_collect方法前,会先执行GenCollectedHeap::save_marks方法,因此年轻代的此方法返回true
bool DefNewGeneration::no_allocs_since_save_marks() {
  assert(eden()->saved_mark_at_top(), "Violated spec - alloc in eden");
  assert(from()->saved_mark_at_top(), "Violated spec - alloc in from");
  return to()->saved_mark_at_top();
}
 
//DefNewGeneration::save_marks会将该属性置为top
bool saved_mark_at_top() const { return saved_mark_word() == top(); }

7、collect
collect就是DefNewGeneration执行GC的核心方法了,其中根节点遍历由GenCollectedHeap::gen_process_roots实现,找到的根节点oop由FastScanClosure处理,如果该oop是年轻代的则执行promote,拷贝到to区或者老年代,然后让该oop指向新的对象地址。除根节点oop之外,还需要遍历新创建的klass,由FastScanClosure以同样的方式处理器java_mirror属性,即对应类的Class实例。遍历根节点完成后gen_process_roots还需要遍历老年代对应的脏的卡表项对应的内存区域中的对象,需要遍历这些对象的同样在脏的内存区域中的引用类型属性,即老年代对象所引用新的年轻代的oop,同样将这些oop拷贝到to区或者老年代,然后让该oop指向新的对象地址;注意遍历脏的卡表项对应内存区域中的对象时使用的FastScanClosure的第二个参数为true,执行完上述操作后需要将这些oop对应的卡表项置为youngergen_card。

本方法主要完成引用遍历前后的处理逻辑,这里重点关注年轻代三个区的使用。参考上一篇DefNewGeneration
中分析的allocate方法及其相关方法的实现可知,年轻代对象分配主要在eden区,只有在eden区和老年代都满了导致promote失败才可能在from区中分配内存,正常情况下to区是空的,GC引用遍历时会遍历eden区和from区的对象,这个过程中如果存活对象的分代年龄小于tenuring_threshold则会拷贝到to区中并增加分代年龄,大于该阈值的就拷贝到老年代中,遍历结束后如果没有promote失败则认为所有存活对象都已成功复制,会清空eden区和from区,然后交换from区和to区,即空的from区变成to区,包含有存活对象的to区变成from区,如此循环往复,to区会一直是空的;如果老年代空间不足,出现promote失败的情形,则eden区和from区存在尚未复制的存活对象,则不能清空此时的eden区和from区,这时需要将eden区和from区中对象的对象头恢复成初始状态,即去掉forward指针,然后交换from区和to区,并且将交换后的to区作为from区的next_compaction_space(正常是null),从而尽可能的利用剩余的年轻代内存空间,此时的to区因为是原来的包含存活对象的from区,所以不是空的。

//此方法的四个参数只有clear_all_soft_refs是有用的参数,如果为true表示会清除所有的软引用
//如果是false则按照默认逻辑处理
void DefNewGeneration::collect(bool   full,
                               bool   clear_all_soft_refs,
                               size_t size,
                               bool   is_tlab) {
  assert(full || size > 0, "otherwise we don't want to collect");
 
  GenCollectedHeap* gch = GenCollectedHeap::heap();
  
  //记录GC的开始时间和原因
  _gc_timer->register_gc_start();
  DefNewTracer gc_tracer;
  gc_tracer.report_gc_start(gch->gc_cause(), _gc_timer->gc_start());
 
  _next_gen = gch->next_gen(this);
 
  //判断老年代是否有足够的空间保存年轻代复制过去的对象
  if (!collection_attempt_is_safe()) {
    if (Verbose && PrintGCDetails) {
      gclog_or_tty->print(" :: Collection attempt not safe :: ");
    }
    //通知GCH老年代空间不足
    gch->set_incremental_collection_failed(); // Slight lie: we did not even attempt one
    //老年代空间不足,终止年轻代的GC
    return;
  }
  assert(to()->is_empty(), "Else not collection_attempt_is_safe");
  
  //将_promotion_failed属性置为false,记录promote失败信息的PromotionFailedInfo重置成初始状态
  init_assuming_no_promotion_failure();
 
  GCTraceTime t1(GCCauseString("GC", gch->gc_cause()), PrintGC && !PrintGCDetails, true, NULL, gc_tracer.gc_id());
  // Capture heap used before collection (for printing).
  size_t gch_prev_used = gch->used();
  
  //设置GC Tracer
  gch->trace_heap_before_gc(&gc_tracer);
 
  SpecializationStats::clear();
 
  //初始化两个遍历器
  IsAliveClosure is_alive(this);
  ScanWeakRefClosure scan_weak_ref(this);
  
  //重置ageTable
  age_table()->clear();
  //重置to区
  to()->clear(SpaceDecorator::Mangle);
  //重置cur_youngergen_card_val,并行遍历脏的卡表项时使用
  gch->rem_set()->prepare_for_younger_refs_iterate(false);
 
  assert(gch->no_allocs_since_save_marks(0),
         "save marks have not been newly set.");
 
  CollectorPolicy* cp = gch->collector_policy();
  
  //FastScanClosure用来遍历年轻代中的存活对象oop,第二个参数为true,表示会将oop对应的卡表项置为youngergen_card
  FastScanClosure fsc_with_no_gc_barrier(this, false);
  FastScanClosure fsc_with_gc_barrier(this, true);
  
  //KlassScanClosure用来遍历在上一次GC到当前GC之间创建的新的Klass对应的Class实例
  KlassScanClosure klass_scan_closure(&fsc_with_no_gc_barrier,
                                      gch->rem_set()->klass_rem_set());
  //CLDToKlassAndOopClosure用来遍历一个ClassLoader加载的所有类对应的Class实例和依赖等                                    
  CLDToKlassAndOopClosure cld_scan_closure(&klass_scan_closure,
                                           &fsc_with_no_gc_barrier,
                                           false);
  //设置promote失败时的遍历器
  set_promo_failure_scan_stack_closure(&fsc_with_no_gc_barrier);
  //FastEvacuateFollowersClosure主要用来遍历被promote到老年代的对象,恢复其对象头并遍历其引用类型属性
  FastEvacuateFollowersClosure evacuate_followers(gch, _level, this,
                                                  &fsc_with_no_gc_barrier,
                                                  &fsc_with_gc_barrier);
 
  assert(gch->no_allocs_since_save_marks(0),
         "save marks have not been newly set.");
  
  //执行根节点遍历以及老年代新应用的oop遍历
  gch->gen_process_roots(_level, //level就是0
                         true,  //因为level是0,所以此参数实际无意义
                         true,  // StrongRootsScope的active入参为true
                         GenCollectedHeap::SO_ScavengeCodeCache, //只遍历nmethod中的oop
                         GenCollectedHeap::StrongAndWeakRoots,//StrongAndWeakRoots是静态常量,值为false,表示会遍历weak root,如StringTable中的String对象
                         &fsc_with_no_gc_barrier,
                         &fsc_with_gc_barrier,
                         &cld_scan_closure);
 
  //遍历所有promote到老年代的对象,恢复其对象头,遍历其引用类型属性
  evacuate_followers.do_void();
 
  FastKeepAliveClosure keep_alive(this, &scan_weak_ref);
  ReferenceProcessor* rp = ref_processor();
  rp->setup_policy(clear_all_soft_refs);
  //处理do_void方法遍历引用类型属性过程中找到的Reference实例,如果该实例的referent对象是存活的,则从待处理列表中移除,否则将referent属性置为null
  const ReferenceProcessorStats& stats =
  rp->process_discovered_references(&is_alive, &keep_alive, &evacuate_followers,
                                    NULL, _gc_timer, gc_tracer.gc_id());
  gc_tracer.report_gc_reference_stats(stats);
 
  if (!_promotion_failed) {
    //promote没有失败的
    //重置清空eden区和from区
    eden()->clear(SpaceDecorator::Mangle);
    from()->clear(SpaceDecorator::Mangle);
    if (ZapUnusedHeapArea) {
      to()->mangle_unused_area();
    }
    //交换from区和to区
    swap_spaces();
    //交换后,原来的from区变成to区,必须是空的
    assert(to()->is_empty(), "to space should be empty now");
    //调整tenuring_threshold
    adjust_desired_tenuring_threshold();
 
    AdaptiveSizePolicy* size_policy = gch->gen_policy()->size_policy();
    //重置gc_overhead_limit_count
    size_policy->reset_gc_overhead_limit_count();
    if (PrintGC && !PrintGCDetails) {
      gch->print_heap_change(gch_prev_used);
    }
    assert(!gch->incremental_collection_failed(), "Should be clear");
  } else {
    //如果存在promote失败的情形
    assert(_promo_failure_scan_stack.is_empty(), "post condition");
    //drain_promo_failure_scan_stack方法会负责处理掉里面保存的oop,遍历其所引用的其他oop,找到的oop的处理逻辑就是fsc_with_no_gc_barrier
    //释放_promo_failure_scan_stack的内存,
    _promo_failure_scan_stack.clear(true); // Clear cached segments.
    //移除from区和eden区包含的对象的froward指针
    remove_forwarding_pointers();
    if (PrintGCDetails) {
      gclog_or_tty->print(" (promotion failed) ");
    }
    //交换from区和to区,注意此时eden区和from区因为promote失败所以不是空的,还有存活对象
    swap_spaces();   // For uniformity wrt ParNewGeneration.
    //将to区作为from区的next_compaction_space,正常为NULL
    from()->set_next_compaction_space(to());
    //将incremental_collection_failed置为true
    gch->set_incremental_collection_failed();
 
    //通知老年代promote失败,CMS老年代实际不做处理
    _next_gen->promotion_failure_occurred();
    gc_tracer.report_promotion_failed(_promotion_failed_info);
  }
  //设置并行遍历时的边界
  from()->set_concurrent_iteration_safe_limit(from()->top());
  to()->set_concurrent_iteration_safe_limit(to()->top());
  SpecializationStats::print();
 
  //更新GC完成时间
  jlong now = os::javaTimeNanos() / NANOSECS_PER_MILLISEC;
  update_time_of_last_gc(now);
 
  gch->trace_heap_after_gc(&gc_tracer);
  gc_tracer.report_tenuring_threshold(tenuring_threshold());
 
  _gc_timer->register_gc_end();
  //更新GC日志
  gc_tracer.report_gc_end(_gc_timer->gc_end(), _gc_timer->time_partitions());
}
 
size_t DefNewGeneration::used() const {
  //to区通常是空的,不计入此处
  return eden()->used()
       + from()->used();      // to() is only used during scavenge
}
 
void DefNewGeneration::init_assuming_no_promotion_failure() {
  _promotion_failed = false;
  _promotion_failed_info.reset();
  from()->set_next_compaction_space(NULL);
}
 
void set_promo_failure_scan_stack_closure(ExtendedOopClosure *scan_stack_closure) {
    _promo_failure_scan_stack_closure = scan_stack_closure;
  }
 
void DefNewGeneration::swap_spaces() {
  //交换from区和to区对应的内存区域
  ContiguousSpace* s = from();
  _from_space        = to();
  _to_space          = s;
  //重置eden区的next_compaction_space
  eden()->set_next_compaction_space(from());
  //将原来的to区的next_compaction_space置为null
  from()->set_next_compaction_space(NULL);
 
  if (UsePerfData) {
    CSpaceCounters* c = _from_counters;
    _from_counters = _to_counters;
    _to_counters = c;
  }
}
 
void DefNewGeneration::adjust_desired_tenuring_threshold() {
  //重置tenuring_threshold,注意此处传入的是to区的容量,因为对象是往to区拷贝的
  _tenuring_threshold =
    age_table()->compute_tenuring_threshold(to()->capacity()/HeapWordSize);
}
 
void DefNewGeneration::remove_forwarding_pointers() {
  RemoveForwardPointerClosure rspc;
  //遍历eden区和from区中的对象,将对象头恢复成初始状态,即去掉原来对象头中包含的forward指针,即该对象拷贝的目标地址
  eden()->object_iterate(&rspc);
  from()->object_iterate(&rspc);
 
  // Now restore saved marks, if any.
  assert(_objs_with_preserved_marks.size() == _preserved_marks_of_objs.size(),
         "should be the same");
  //遍历_objs_with_preserved_marks,恢复其中保存的oop的对象头,里面的对象都是promote失败的对象
  while (!_objs_with_preserved_marks.is_empty()) {
    oop obj   = _objs_with_preserved_marks.pop();
    markOop m = _preserved_marks_of_objs.pop();
    obj->set_mark(m);
  }
  //清空两个栈
  _objs_with_preserved_marks.clear(true);
  _preserved_marks_of_objs.clear(true);
}
 
class RemoveForwardPointerClosure: public ObjectClosure {
public:
  void do_object(oop obj) {
    obj->init_mark();
  }
};
 
  virtual void update_time_of_last_gc(jlong now)  {
    _time_of_last_gc = now;
  }

相关文章

网友评论

      本文标题:DefNewGeneration 之二

      本文链接:https://www.haomeiwen.com/subject/febnxdtx.html