分享

JAVA 10源码阅读笔记之JEP-307(G1的并行Full GC)

 印度阿三17 2018-09-27

# 1. 背景
JEP-307解决了G1垃圾回收器的一个严重的问题,截止到Java 9,G1的Full GC采用的是单线程算法,严重影响性能,无法利用到多核能力进行垃圾回收。JEP-307修复了此问题,发生Full GC时允许使用多个线程进行并行回收。

# 2. G1的简单介绍
在Java 6/7 时代,大量线上服务采用CMS垃圾回收算法,以牺牲吞吐量为代价获得较少的STW时间。但是由于CMS的特质和硬件的发展,带来了如下两个问题:
* 大堆性能较差
* 由于CMS是标记清理,导致老年代空间无可避免走向碎片化,直到并发模式失败或晋升失败,触发Full GC

为了解决CMS / Parallel的一系列问题,hotspot最早在Java SE 6 Update 14中引入G1,并在JAVA9中将默认GC算法切换为G1(JEP-248)

针对上文CMS的两个问题,G1有了相应的优化:
* 将堆切分为若干个region进行管理(如 2000个region)
* 对象疏散时,伴随着region级别的内存整理,降低了发生Full GC的概率

然而G1仅仅是降低了Full GC的概率,并不能完全避免。当并发模式失败、晋升失败、疏散失败、巨型对象分配失败时,仍然无可避免的会触发STW。而此处的Full GC由于历史原因仅使用一个线程,因此会严重的影响性能,JEP-307针对此问题专门进行了优化。

G1和CMS的详细介绍网上资料很多,此处不再赘述。

# 3. changeset 47885:5caa1d5f74c1
OpenJDK / jdk10 / hotspot 中关于G1优化的改动主要在changeset http://hg.openjdk./jdk/jdk/rev/5caa1d5f74c1

## 3.1 G1CollectedHeap
G1CollectedHeap.cpp是对G1堆管理的抽象,处理除了TLAB堆内存分配外的堆内存分配,Full GC的触发逻辑也在此接口中

2017年8月份提交的changeset 13452:4d2fded7bd7d中添加了G1SerialFullCollector.cpp,将单线程Full GC算法抽象到此文件中,并在注释中声明
```
// Temporarily make discovery by the STW ref processor single threaded (non-MT)
    G1SerialFullCollector serial(scope, ref_processor_stw());
    serial.prepare_collection();
    serial.collect();
    serial.complete_collection();
```

在JEP-307中,相关代码被修改为,除了引用处理类外,新增了mark bitmap和工作线程数入参
```
    G1FullCollector collector(scope, ref_processor_stw(), concurrent_mark()->next_mark_bitmap(), workers()->active_workers());
    collector.prepare_collection();
    collector.collect();
    collector.complete_collection();
```

## 3.2 G1FullCollector
G1FullCollector是对G1 Full GC逻辑的封装。 主要代码改动在回收(collect)阶段
```
void G1FullCollector::collect() {
  // 对象标记阶段
  phase1_mark_live_objects();
  verify_after_marking();

  deactivate_derived_pointers();

  // 准备阶段
  phase2_prepare_compaction();

  // 指针调整阶段
  phase3_adjust_pointers();

  // 整理阶段
  phase4_do_compaction();
}
```

使用G1CollectedHeap的线程组并行执行任务
```
void G1FullCollector::run_task(AbstractGangTask* task) {
  G1CollectedHeap::heap()->workers()->run_task(task, _num_workers);
}
```

### 3.2.1 标记阶段
```
void G1FullCollector::phase1_mark_live_objects() {
  // 从GC root递归遍历所有活动对象并标记
  GCTraceTime(Info, gc, phases) info("Phase 1: Mark live objects", scope()->timer());

  // 多线程标记
  G1FullGCMarkTask marking_task(this);
  run_task(&marking_task);

  G1FullGCReferenceProcessingExecutor reference_processing(this);
  reference_processing.execute(scope()->timer(), scope()->tracer());

  {
    GCTraceTime(Debug, gc, phases) trace("Phase 1: Weak Processing", scope()->timer());
    WeakProcessor::weak_oops_do(&_is_alive, &do_nothing_cl);
  }

  // 类的卸载和清理
  if (ClassUnloading) {
    GCTraceTime(Debug, gc, phases) debug("Phase 1: Class Unloading and Cleanup", scope()->timer());
    bool purged_class = SystemDictionary::do_unloading(&_is_alive, scope()->timer());
    G1CollectedHeap::heap()->complete_cleaning(&_is_alive, purged_class);
  } else {
    GCTraceTime(Debug, gc, phases) debug("Phase 1: String and Symbol Tables Cleanup", scope()->timer());
    // 如果不卸载类,就只清理字符串常量和符号
    G1CollectedHeap::heap()->partial_cleaning(&_is_alive, true, true, G1StringDedup::is_enabled());
  }

  scope()->tracer()->report_object_count_after_gc(&_is_alive);
}
```
### 3.2.2 准备阶段
```
void G1FullCollector::phase2_prepare_compaction() {
  GCTraceTime(Info, gc, phases) info("Phase 2: Prepare for compaction", scope()->timer());
  // 调用prepare_compaction_common方法
  prepare_compaction_ext(); 
}

void G1FullCollector::prepare_compaction_common() {
  G1FullGCPrepareTask task(this);
  run_task(&task);

  // 如果没有可用region,为了避免OOM,退化为串行处理
  if (!task.has_freed_regions()) {
    task.prepare_serial_compaction();
  }
}
```

### 3.2.3 调整阶段
```
void G1FullCollector::phase3_adjust_pointers() {
  // 指针指向新地址,调整记录集
  GCTraceTime(Info, gc, phases) info("Phase 3: Adjust pointers and remembered sets", scope()->timer());

  G1FullGCAdjustTask task(this);
  run_task(&task);
}
```

### 3.2.4 整理阶段
```
void G1FullCollector::phase4_do_compaction() {
  // 根据准备阶段创建的整理队列并行整理
  GCTraceTime(Info, gc, phases) info("Phase 4: Compact heap", scope()->timer());
  G1FullGCCompactTask task(this);
  run_task(&task);

  // 如果可用region很少,为了避免OOM,退化为串行处理
  if (serial_compaction_point()->has_regions()) {
    task.serial_compaction();
  }
}
```

# 4. 相关链接
JEP-307 http://openjdk./jeps/307

来源:http://www./content-1-26791.html

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多