G1回收器介绍
Garbage-First (G1)垃圾回收器适用于“CPU多核、大内存”的服务器。它尝试以高概率满足垃圾收集(GC)暂停时间目标,同时实现高吞吐量 。
G1回收器将heap分成一组大小相等的region(大约2000个),每个region的大小固定在1~32MB(必须为2的幂数)。每个region的内存是物理连续的,不同的region内存物理地址不一定连续,但是同generation的多个region是逻辑连续的。 这就是G1“区域化”。
一组region被逻辑划分成“young generation(含eden区、survivor区)”、“old generation(含old region,humongous region”。young generation和old generation的内存地址不是固定不变的,会应用的运行,由jvm在Xmx范围动态调整。
注意:G1中没有survivor0 和 survivor1之分,只有一个,大小是动态分配的。
什么是RSets?
每个Region初始化时,会初始化一个remembered set(已记忆集合),该集合用来记录并跟踪其它Region指向该Region中对象的引用。
如下:Region1和Region3中有对象引用了Region2的对象,则在Region2的Rset中记录了这些引用。
在垃圾回收时,根据Rsets即可知道一个region中的对象被哪些region引用,这样可以避免扫描整个堆来找到可以回收的垃圾。
什么是Csets?
垃圾回收器计划回收的region集合。
YoungGC过程?
步骤1. 选择收集集合(Collection Set),G1会在遵循用户设置的GC暂停时间上限的基础上,选择一个最大年轻代region数,将这个数量的所有年轻代区域作为收集集合。
步骤2. 根处理(Root Scanning),接下来,需要从GC ROOTS遍历,查找从ROOTS直达到收集集合的对象,移动他们到Survivor区域的同时将他们的引用对象加入标记栈
步骤3. RSet扫描(Scan RS),将RSet作为ROOTS遍历,查找可直达到收集集合的对象,移动他们到Survivor区域的同时将他们的引用对象加入标记栈
步骤4. 移动(Evacuation/Object Copy),遍历上面的标记栈,将栈内的所有所有的对象移动至Survivor区域(其实说是移动,本质上还是复制)
注意:YoungGC过程中,存活对象copy到survivor区或者old区的过程是需要STW的。
eden区和survivor区的大小是动态计算的,可能会一直在变动过程中。
触发条件:当JVM无法将新对象分配到eden区域时,会触发年轻代的垃圾回收(年轻代垃圾回收是完全暂停的,虽然部分过程是并行,但暂停和并行并不冲突)。也会称为“evacuation pause”
G1回收器回收old generation的标记过程
- 初始标记阶段(Initial Mark):需要STW。 对于G1,该过程依附在一个正常的Young GC上。 标记survivor区域的GCroots,这些区域可能引用old generation中的对象。
- GCroots扫描阶段(root scan):扫描initial mark阶段标记出的GC roots关联的survivor区的对象,以及这些对象引用old generation的对象。标记出引用的对象。
- 并发标记阶段(concurrent mark):在整个heap中标记存活对象(live object)。该过程和应用线程并发执行不需要STW,也可以被young GC打断。
- 重新标记阶段(remark):需要STW。使用'SATB[snapshot-at-the-beginning]'算法再次标记前述几个过程中断开的引用,避免漏标。 <解决了CMS可能的垃圾漏标问题>
- 清理阶段(cleanup):
- 统计存活对象,根据设定的目标停顿时间,确定要回收的region。该过程需要STW
- 清空Remembered Sets("Rsets")。该过程需要STW
- 重置空的region,并将其加到空白列表中。该过程是concurrent的。
什么是MixedGC?
mixedGC,即混合GC,针对年轻代和老年代都进行垃圾回收。mixedGC是G1垃圾回收器中特有的概念。
触发条件:一旦老年代占据堆内存的 45%(-XX:InitiatingHeapOccupancyPercent:设置触发标记周期的 Java 堆占用率阈值,默认值是 45%。),就要触发 Mixed GC的标记周期。
什么是full gc?
对整个堆进行回收,包括新生代,老年代、metaspace等。
触发条件:当mixedGC回收内存的速度无法跟上内存分配的速度,导致老年代也满了,就会进行Full GC对整个堆进行回收。G1中的Full GC也而是单线程串行的,而且是全暂停,使用的是标记-整理算法,代价非常高。
G1回收器引入STW的几种情形?
- copy存活对象到新的region时;(如YoungGC,MixedGC)
- initial mark过程
- remark过程
- cleanup的部分动作(确定要清空的region,以及确定下次回收的old generation region的"候选人")
官方文档: