java G1(并发)垃圾收集器(二)

官方文档:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html#sthref50

G1提供了两种GC模式,Young GC和Mixed GC,两种都是Stop The World(STW).

一:定义

STW

Stop-The-World机制简称STW:在执行垃圾收集时,Java的所有线程被挂起,暂时停顿,只执行垃圾回收。-XX:MaxGCPauseMillis=200,参数可设置最大暂停时间,jdk8默认最大暂停时间是200ms

mutator

在垃圾回收的里,mutator指应用程序。直译:赋值函数

SATB:Snapchat-At-The_Beginning

SATB是维持并发GC的一种手段。G1并发的基础就是SATB。SATB可以理解成在GC开始之前对堆内存里的对象做一次快照,此时活的对象就认为是活的,从而形成一个对象图。

新创建的对象:

在GC收集的时候,eden区的新new的对象认为是活的对象,其实就是eden区的prevTAMS和nextTAMS间的对像。每个region记录着五个指针:bottom、previous TAMS、next TAMS、top和end,其中prevTAMS和nextTAMS。previous TAMS和next TAMS是前后两次发生并发标记时的位置,在两个TAMS间的对象就是新分配的,活的对象。

GC过程中引用发生变化:

GC过程中引用发生变化通过Write Barrier进行处理。Write Barrier就是对引用字段进行赋值做了环切,了解到引用对象发生的变化。

 

logging write barrier

在引用赋值的时候执行的逻辑。因为引用赋值操作非常的频繁,所以把每个引用赋值操作当作一个log事件上,记录到一个队列中,这个队列被异步的后台线程读取,获得所有的引用赋值信息。

SATB write barrier
每个 Java 线程有一个独立的、定长的 SATBMarkQueue,在写屏障里只把旧引用压入该队列中。满了之后会加到全局 SATBMarkQueueSet。

 

RSet:Remembered Set

RSet用于记录从非收集部分指向收集部分的指针的集合的抽象数据结构。其实就是标记每个eden区被old区的哪些对像引用,因为被引用的对象要作为根被保丢。old区以卡表的形式记录,具体对像的地址。卡表其实就是数组,以下标的形式标记索引值。简单模拟eden区RSet的值如下:

{"对像1":"old区1-卡表索引1","对像1":"old区1-卡表索引1","对像1":"old区1-卡表索引1","对像1":"old区1-卡表索引1".....}


卡表索引值对应具体的内存地址,卡表则记录当前地址是否被引用。
简单模拟old区卡表索引的值如下

{"index-1":"address:0x768376529172":"0","index-2":"address:0x768376529172":"","index-3":"address:0x768376529172":"0",}

CSet:Collection Set

CSet记录的是GC要收集的Region的集合,CSet里的Region可以是任意代的。在GC的时候,对于old->young和old->old的跨代对象引用,只要扫描对应的CSet中的RSet即可。

拷贝存活对象:STW机制下运行

使用根搜索算法扫描所有eden区和surviver区可用的对像,幸存会被copy到一个新的region.根据对像的年龄(或者说被触发Minor gc的次数)来决定,会被copy到区域的类型.
1)经历16次Young GC的对象,被copy到old区.
2)未经历16次Young GC的对象被copy到surviver区。如果surviver区空间不够,则直接被copy到old区。
3)不同区间引用的处理:
old区的空间远大于eden区,Young GC是不扫描old区的,在不同区的copy中,eden区和old区会被互相引用,通过RSet的方式确认每个对像是否为根。在GC处理时,通过遍历Rset所指定的所有索引则可以找到所有根搜索算法的根对像。

三色标记算法

tracing GC将对象分为三类:白色(垃圾收集器未探测到的对象)、灰色(活着的对象,但是还没有被垃圾收集器扫描过)、黑色(活着的对象,并且已经被垃圾收集器扫描过)。垃圾收集器的工作过程,就是通过灰色对象的指针扫描它指向的白色对象,如果找到一个白色对象,就将它设置为灰色,如果某个灰色对象的可达对象已经全部找完,就将它设置为黑色对象。当在当前集合中找不到灰色的对象时,就说明该集合的回收动作完成,然后所有白色的对象的都会被回收。

二:Young GC

Young GC主要是对Eden区进行GC,在Eden空间耗尽时会被触发,对从上次垃圾收集后的eden区和surviver区进行处理,控制young GC开销的手段是动态改变Eden区的个数;
1)扫描静态和本地对象:对所有eden区对像和CSet区的对像进行扫描。
在GC的时候,对于old->young和old->old的跨代对象引用,只要扫描对应的CSet中的RSet即可。
2)根据根扫描数据更新RSet
3)检测从eden区被old区引用的对象,并标记
4)选择CSet(包括eden区surviver区)拷贝存活的对象到survivor/old区域

三:Mix GC

Mix GC执行了Young GC并回收部分后台扫描线程标记的old区,控制GC开销的手段是动态改变old区的个数,old区的数据全在mix GC处理。

包括两部分
1:全局并发标记(global concurrent marking)
2:根据并发标记的数据,选择CSet(包括eden区surviver区和old区)进行拷贝存活对象(STW)
而这两部分可以相对独立的执行。

四:全局并发标记:


1)初始标记(initial mark phase)

在此阶段,old区和surviver区为空,GC对根进行标记,该阶段会跟一次young GC一起进行,所以会经历STW,会比正常的young GC更耗时。

2)根区域扫描(root region scan phase)

在初始标记的存活区扫描对老年代的引用,并标记被引用的对象,扫描所有eden区和survivor区及old区被引用的根集合,将所有通过根集合直达的对象压入扫描栈,等待后续的处理。该阶段与应用程序同时运行,并且只有完成该阶段后,才能开始下一次 STW 年轻代垃圾回收。

3)并发标记(Concurrent Marking phase)

扫描在整个堆中查找和标记可访问的(存活的)对象,包括SATB write barrier所记录下的引用,将其字段压入扫描栈,并且递归扫描。该阶段与应用程序同时运行,有可能被 STW 年轻代垃圾回收中断

4)最终标记(Remark phase)

清空 SATB 缓冲区,跟踪未被访问的存活对象,并执行引用处理。因为一个线程的SATBMarkQueue要满了之后才会被加入到全局的队列中处理,那么在并发标记结束后,会有线程中的SATBMarkQueue是不满且未清空的.SATBMarkQueue中未处理的引用,会在这个阶段处理。

5)清除垃圾(Cleanup phase)

执行统计和 RSet 净化的 STW 操作,通过统计每个region里的活的对象有多少,识别完全空闲的region和可供进行Mix GC的region,发现完全空闲的region就会将其整体回收到可分配region列表中。

 

五:G1 GC 工作流程

分代式G1的正常工作流程就是在young GC与mixed GC之间视情况切换,背后定期做做全局并发标记。在资源不够的情况下会触发Full GC(Serial GC).

Initial marking默认搭在young GC上执行;当全局并发标记正在工作时,G1不会选择做mixed GC,反之如果有mixed GC正在进行中G1也不会启动initial marking。

G1 GC 会标识整个堆的所有对像,但会控制Young GC和Mix GC的CSet内region的数量大小,因而控制系统资源的占用。

六:Full GC的触发原因:

 

  • 并发模式失效

G1启动并发标记周期,但是在mix gc之前,old区就被填满了,这时候G1就会放弃标记周期,改为执行Full gc,对应的gc日志为:[GC concurrent-mark-abort]
解决办法:发生这种失败意味着堆的大小应该增加了,或者G1收集器的后台处理应该更早开始,或者需要调整周期,让它运行得更快(如,增加后台处理的线程数)。

  •  晋升失败

G1在进行新生代gc时,老年代没有足够的内存提供给晋升对象,将会触发Full gc。对应的gc日志为:to-space exhausted
解决这种问题的方式是:
a. 增加 -XX:G1ReservePercent 选项的值(并相应增加总的堆大小),为“目标空间”增加预留内存量。
b. 通过减少 -XX:InitiatingHeapOccupancyPercent 提前启动标记周期。
c. 也可以通过增加 -XX:ConcGCThreads 选项的值来增加并行标记线程的数目。

  • 疏散失败

进行新生代gc时,survivor和老年代没有足够的空间容纳存活的对象。对应的gc日志为: to-space overflow
解决办法与4.2一样。

  • 巨型对象分配失败

巨型对象分配失败也会触发Full gc,解决办法:增大regionSize

  • metaspace gc

metaspace大小达到阈值(metaspaceSize大小,是动态的),会触发Full gc

七:write barrier非白算法

G1 GC有两种并发执行事件:
(1) 全局并发标记;
(2) logging write barrier的部分处理。
根据三色标记算法:黑色是自己已标记且字段也全部标记了的对象(collector就不会再访问到它了),灰色是自己已标记但尚有字段未标记的对象(collector正在访问的对象),白色是尚未标记的对象。
如果在并发标记过程中,应用程序(mutator)将没有被灰色对象直接或间接引用的白色对象设置为黑色对象的引用.则有可能会造成遗漏扫描白对象.但此时write barrier会记录log事件。
SATB的作法是在write barrier里把所有旧的引用所指向的对象都变成非白的,是黑色不处理,否则设置为灰色。以此为保证不会遗漏扫描.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值