包含内容
- G1 回收原理,优势
- G1后为什么有ZGC
G1
简述
- 出现背景
在发生Minor GC时,由于Survivor区已经放不下去,多出对象只能通过代际提升(generation promtion)到老年代。 但是,糟糕的是,老年代因为空间碎片的缘故,会发生concurrent mode failure而无法满足及时响应,影响用户体验.此时会老年代就会降级为Serial old垃圾收集器进行收集。 Serial old垃圾收集器收集的后果比发生concurrent model failure更严重,因为其有major GC发展成 Full GC造成更长停顿,且时间未知
- 问题图示
G1解决方法
-
定义一个停顿时间,然后反向推断收集内容。如领导年初制定Key Performance Indicator.然后将GC任务分解成一个个小任务,每次清理一些垃圾,保证其不会造成过长停顿,最后将垃圾收集完毕
G1要求在任意1s内,停顿不得超过10ms,即给给他制定KPI。G1会近年来达成这个目标。它能够推算出本次要收集的大体区域,已增量方式完成收集 -
设置G1参数,制定1s内GC停顿时间 -XX:MaxGCPauseMillis=10
G1 垃圾收集器
- 目标替换CMS,一款软实时垃圾回收器。参数较CMS的72个,减少至26个
- 全程Garbage First GC.为达到上述KPI,其在堆的划分与CMS等垃圾收集器不同。
其他的垃圾收集器,都是对某个年代的整体收集(minor gc对年轻代,major GC对老年代,Full GC是所有堆) G1 将堆划分为很大方,每一份当做一个小目标,部分上目标易达成 G1在逻辑上仍有有年轻代,年老代划分
- G1 逻辑上的年轻代,年老代划分
1.G1也有Eden区和Survivor区概念,只是内存上不再连续,有一小份一小份组成。 2.一小份区域大小固定,大小一致(1M~32M间的2的幂指数),叫作Region-小堆区。可以是Eden区,Survivor区,old区。 3.如果对象过大,一个Region无法放置,上图中有个很大黄色区域,叫Humongous Region,大小超过Region 50%对象,会分配在此。 4.Region大小设置参数: -XX:G1HeapRegionSize=<N>M
- G1 代际空间划分
- 回收策略,优先回收垃圾最多的Region
G1回收过程
代际回收
````
逻辑上G1分年轻代和年老代,但其比例不固定。为了达到-XX:MaxGCPauseMillis规定效果,G1会自动调整二者比例。
如果强行指定-Xmn或-XX:NewRation去设定年轻代和年老代比例,G1设置的-XX:MaxGCPauseMillis会失效。
````
G1回收流程
- (1)G1”年轻代回收”,同样叫Minor GC,如前和CMS类似,发生时机是Eden区满时。
- (2)老年代垃圾收集,严格说不是收集,是一个”并发标记”过程,顺便清理一点点对象。
- (3)真正清理发生在”混合模式”,它不止清理年轻代,还会将老年代一部分区域清理。
- GC日志中,(1)过程叫[GC pause(G1 Evacuation Pause) (young)],(2)过程是[GC pause(G1 Evacuation Pause) (mixed) ],Evacuation 是转移的意思,和copy意思类似。
- (1),(2),(3)三者模式间隔不固定,如1次minor gc后,发生2次并发标记,接着发生7次 mixed gc
Remembered Set
- 简称Rset,一个空间换时间的数据结构
- 同CMS中的卡表(Card Table)类似,解决跨代引用的问题,记录和维护Region之间的对象引用关系
- 和Card Table不同,Rset是记录了其他Region中对象对本Region的对象引用,属于point-into(谁引用了我Region内的对象)。而Card Table是point-out(我引用了谁的对象,老年代的对象引用了哪个年轻代的对象,卡表对应的老年代对象卡页会标记dirty)。Rset有点倒排索引的意味
- 可以将Rset理解成Hash,可以是引用的本Region对象的地址,value是对应的卡页集合(其他Region对象引用该Region对象的集合)
- RSet记录关系图示
- Rset 避免对整个堆区所有Region进行扫描,是的部分手机成为可能
- 年轻代Region的Rset只保存老年代的引用(对应的卡页),这是因为年轻代回收时针对所以年轻代Region,没必要年轻代对象记录其他年轻代对象,故年轻代Rset有可能为空(无老年代引用他)。
- 年老代Region的Rset只保存年老代对象对它的引用。因为老年代回收前会先对年轻代进行回收。这是Eden变空了,而回收过程中会扫描Survivor分区,所以没必要保存年轻代的引用
- Rset通常占用较大空间,5%或者更高,计算开销也较大。
- 为维护Rset,程序运行时,写入某个字段就会产生一个post-write barrier。为了减少该开销,将内容放入Rset过程是异步,经过诸多优化:Write Barrier把脏卡信息放到本地缓冲区,使用专门GC线程收集脏卡信息,并将其传给被引用Region的Rset
- 参数-XX:G1ConcRefinementThreads或-XX:ParallelGCThreads可以控制这个异步过程。如果并发优化线程更不是缓冲区速度,就会在用户进程完成。
G1 具体回收过程
- G1还有一个CSet概念,全称collection set,即收集集合,保存一次GC中执行垃圾回收的Region.GC是在Cset中所有存活数据(live data)都会被转移
年轻代回收
-
年轻代是一个STW过程,它的跨代引用通过RSet记录来追溯,会一次性回收掉所有Region。
-
JVM启动时,G1会先准备好Eden区,程序在运行过程不断在Eden创建对象,当所有Eden区都满,G1启动一次年轻代回收过程(STW)
-
年轻代收集包括下面回收阶段
1.扫描根: 根可看做前面介绍的GCRoots,加上RSet记录的其他Region的外部引用 2.更新RSet: 同CMS处理dirty card queue中的卡页类似,更新RSet。此阶段完成后,RSet可以准确的反映老年代对所在的内存分段中对象的引用 3.处理RSet: 识别被老年代对象指向的Eden中对象,这些被指向的Eden中对象被认为是存活对象 4.复制对象: 收集算法依然是Copy算法,对象复制进行内存回收。此阶段对象树被遍历,Eden区内存段中存活对象会被复制到Survivor区的空的Region,过程和其他垃圾回收算法一样,包括对象年龄和晋升。 5.处理引用: 处理Soft,Weak,Phantom,Final,JNI Weak等引用。结束收集
-
年轻代收集流程
-
年轻代对象在内存中回收流程
并发标记
- 当整个队使用达到一定比例(默认为45%%),并发标记阶段就会被启动。这个比例可以通过-XX:InitiatingHeapOccupancyPercent进行配置
- Concrrent Marking为Mixed GC提供标记服务,并不是一次GC过程的一个必须环节。这个过程与CMS的并发可取消预清理相似,是非必须。具体流程如下
1.初始标记(initial Mark) 共用了Minor GC的暂停,这是因为他们可以复用root scan。也是STW的,但是标记与GC Roots直接关联的对象。时间较短 2.Root 区扫描(Root Region Scan) 3.并发标记 该阶段从GC Roots开始对heap中对象标记,标记线程与应用线程并发执行,并且手机各个Region存活对象信息 4.重新标记(Remarking) 和CMS相似,也是STW。标记那些在并发标记阶段发生变化的对象。 5.清理阶段(Cleanup) 该过程不需要STW.如果发现Region里面全是垃圾,在这个阶段立马被清除。不全是垃圾的Region,并不会马上被处理,会在Mixed GC阶段,进行清理。
- G1的并发标记如何应对新的对象变化
由算法SATB保证。STAB全称是Snapshot At The Beginning ,它作用是保证在并发标记阶段的正确性 快照是逻辑上的,主要有介个指针,将Region分成多个区域。如图,并发标记期间分配的对象会在Next TAMS和top之间。 
混合回收(Mixed GC)
- 内并发清理年老代中整个整个的小堆区是一种最优情形。混合收集不知清理年轻代,还会将一部分区域也加入到CSet中。
- 通过Concurrent Marking阶段,我们统计年老代垃圾占比。在Minor GC之后,若该占比超过-XX:G1HeapWastePercent(默认堆大小的5%)参数设置的阈值,下次就会触发Mixed GC。因为这种情况下,GC耗时较大,内存回收有限。所以此参数可以调整Mixed GC的频率。
- 还有参数G1MixedGCCountTarget,用于控制一次并发标记后,最多执行Mixed GC次数。
ZGC
G1的trade-off
- 归功于G1预测模型和抽新的小堆(Region)分区模式。线上严重GC问题有效减少
- 但是如果定下严苛KPI和预测模型失效,或应用内存非常吃紧,内存进行一部分一部分的回收根本不够,始终要进行整个heap的回收。那么G1要做的工作量一点也不比其他垃圾收集器少,而且本身算法复杂,效率要比其他垃圾收集器更差
ZGC
- ZGC优势
1.停顿时间同G1,不超过10ms
2.避免像G1停顿时间不会随着堆的增大,始终维持在10ms下。
3.可支持几百MB ,甚至几T堆大小(最大4T)
4.完全没有年轻代和年老代,只分为一块块page,每次进行GC,都会对page进行压缩操作,所以无碎片问题。
5.ZGC能感知NUMA架构,提供内存访问速度
6.与传统收集算法相比,ZGC直接在对象引用指针做文章,用来标记对象状态,所以只能在64位机器上。
7.只能在Linux使用,使用优势明显,等待普及
- ZGC收集算法策略
小结
- 着重查看数据结构RSet
- 相对CMS,G1有了更可靠,更容易驾驭度。配置参数大幅减少26个。而且有RSet,SATB等算法支持,Remark阶段效率更高。
- G1最重要概念是Region。它采用分而治之,部分收集。对年代用minor GC。堆达到一定阈值-XX:G1HeapWastePercent时,使用Mixed GC。尽力达到设定的停顿目标
- G1 垃圾回收分三种,其中部分标记,为更加复杂的Mixed GC阶段做了充足准备
- JVM G1垃圾收集器参数样例
JAVA_OPTS=”$JAVA_OPTS -XX:NewRatio=2(新生代占比) -XX:G1HeapRegionSize=8m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -XX:MaxTenuringThreshold=10(新生代年龄提升值老年代阈值) -XX:+UseG1GC -XX:InitiatingHeapOccupancyPercent=45 -XX:MaxGCPauseMillis=200 -version:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintReferenceGC -XX:GCLogFileSize=32m -Xloggc:./var/run/gc.log.$(date +%Y%m%d%H%M) -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./var/run/heap-dump.hprof -Dfile.encoding=UTF-8 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sum.management.jmxremote.authenticate=false”