Skip to content

Latest commit

 

History

History
85 lines (67 loc) · 5.66 KB

G1.md

File metadata and controls

85 lines (67 loc) · 5.66 KB

简述G1收集器原理

开启选项:-XX:+UseG1GC

  • G1的设计原则是"收集尽可能多的垃圾(Garbage First)"。

一. 应用场景设计

  • 可以像CMS收集器一样可以和应用并发运行
  • 压缩空闲的内存碎片,却不需要冗长的GC停顿
  • 对GC停顿可以做更好的预测
  • 不想牺牲大量的吞吐量性能
  • 不需要更大的Java Heap

与CMS相比,更优秀的解决方案:

    1. 压缩空间:G1会压缩空闲内存使之足够紧凑,做法是用regions代替细粒度的空闲列表进行分配,减少内存碎片的产生。
    1. 预测STW时间:G1的STW更可控,G1在停顿时间上添加了预测机制,用户可以指定期望停顿时间。

二. 内存布局

  • 在G1中堆被分成一块块大小相等的heap region,一般有2000多块,这些region在逻辑上是连续的。
  • 每块region都会被打唯一的分代标志(eden,survivor,old)。

三. G1中的GC

YGC+MIXGC:G1保留了YGC并加上了一种全新的MIXGC用于收集老年代。G1中没有Full GC,G1中的Full GC是采用serial old Full GC。

3.1 YGC

  • 触发时间:当Eden空间被占满之后,就会触发YGC。
  • 执行方法:在G1中YGC依然采用复制存活对象到survivor空间的方式,当对象的存活年龄满足晋升条件时,把对象提升到old generation regions(老年代)。
  • G1控制YGC开销的手段是动态改变young region的个数,YGC的过程中依然会STW(stop the world 应用停顿),并采用多线程并发复制对象,减少GC停顿时间。

3.2 YGC是否需要扫描整个老年代?

  • 在YGC时,老年代中的对象是不回收的,GC ROOTS里面应包含了老年代中的对象。
  • 但扫描整个老年代会很耗费时间,势必影响整个GC的性能!。
  • G1参照CMS中使用了Card Table的结构(里面记录了老年代对象到新生代引用),采用了一种新的数据结构 Remembered Set 简称Rset。
  • RSet记录了其他Region中的对象引用本Region中对象的关系,属于points-into结构(谁引用了我的对象)。
  • 而Card Table则是一种points-out(我引用了谁的对象)的结构。
  • RSet究竟是怎么辅助GC的呢?
    • 在做YGC的时候,只需要选定young generation region的RSet作为根集,这些RSet记录了old->young的跨代引用,避免了扫描整个old generation。
  • 所以G1中YGC不需要扫描整个老年代,只需要扫描Rset就可以知道老年代引用了哪些新生代中的对象。

3.3 MIXGC

MIXGC回收的内存区域是新生代+老年代。

全局并发标记

全局并发标记过程分为五个阶段

  1. Initial Mark初始标记 STW
  • Initial Mark初始标记是一个STW事件,其完成工作是标记GC ROOTS 直接可达的对象。并将它们的字段压入扫描栈(marking stack)中等到后续扫描。
  1. Root Region Scanning 根区域扫描
  • 不需要STW。根区域扫描是从Survior区的对象出发,标记被引用到老年代中的对象,并把它们的字段在压入扫描栈(marking stack)中等到后续扫描。
  • Root Region Scanning必须在YGC开始前完成。
  1. Concurrent Marking 并发标记
  • 不需要STW。不断从扫描栈取出引用递归扫描整个堆里的对象。每扫描到一个对象就会对其标记,并将其字段压入扫描栈。重复扫描过程直到扫描栈清空。
  • 过程中还会扫描SATB write barrier所记录下的引用。
  1. Remark 最终标记 STW
  • STW操作。在完成并发标记后,每个Java线程还会有一些剩下的SATB write barrier记录的引用尚未处理。这个阶段就负责把剩下的引用处理完。同时这个阶段也进行弱引用处理(reference processing)
  1. Cleanup 清除 STW AND Concurrent
  • STW操作,清点出有存活对象的Region和没有存活对象的Region(Empty Region)
  • STW操作,更新Rset
  • Concurrent操作,把Empty Region收集起来到可分配Region队列。

YGC与MIXGC都是采用多线程复制清除,整个过程会STW。 G1的低延迟原理在于其回收的区域变得精确并且范围变小了。

3.4 STAB

STAB全称为snapshot-at-the-beginning,其目的是了维持并发GC的正确性。

  • GC的正确性是保证存活的对象不被回收,换句话来说就是保证回收的都是垃圾。
    • 如果标记过程是STW的话,那GC的正确性是一定能保证的。但如果一边标记,一边应用在变更堆里面对象的引用,那么标记的正确性就不一定能保证了。
  • STAB实现:
    • GC开始时对内存进行一个对象图的逻辑快照**(snapshot)**,
    • 通过GC Roots tracing 参照并发标记的过程,只要被快照到对象是活的,那在整个GC的过程中对象就被认定的是活的,即使该对象的引用稍后被修改或者删除。
  • 同时新分配的对象也会被认为是活的,除此之外其它不可达的对象就被认为是死掉了。
  • 这样STAB就保证了真正存活的对象不会被GC误回收,但同时也造成了某些可以被回收的对象逃过了GC,导致了内存里面存在浮动的垃圾(float garbage)。

四. 总结

  1. G1把内存分成一块块的Region,每块的Region的大小都是一样的。
  2. G1保留了YGC并加上了一种全新的MIXGC用于收集老年代。
  • 通过控制young gen的region个数来控制young GC的开销。
  • 在MIXGC中的Cset是选定所有young gen里的region,外加根据global concurrent marking统计得出收集收益高的若干old gen region。
  • YGC与MIXGC都是采用多线程复制清除,整个过程会STW。
  1. G1的低延迟原理在于其回收的区域变得精确并且范围变小了。
  2. 全局并发标记分的五个阶段。
  3. 用STAB来维持并发GC的准确性。