1、前言
传统的垃圾收集器将堆内存区域分为新生代和老年代,新生代又分为 Eden 区和 S(from、to)区,如下图所示:
传统堆分代
但是 G1 却采用了新的内存结构,按照 region 分区,每个 region 可以作为 eden、survivor、old、humongous(大对象):
G1 的 region 分区
2、几个重要的概念
region:G1 的堆内存按照 region 分区,每个 region 可以作为 eden、survivor、old、humongous(大对象)。
SATB(Snapshot-At-The-Beginning):字面意思是 GC 开始时活着的对象的一个快照。它主要用在三色算法并发标记后的再标记过程,防止并发标记的过程中用户进程改变白色对象的引用使其错误的被收集(比如将活跃的对象改到已标记的黑色对象上)。
Rset:
全称是Remembered Set,是辅助GC过程的一种结构,典型的空间换时间工具,和Card Table有些类似。还有一种数据结构也是辅助GC的:Collection Set(CSet),它记录了GC要收集的Region集合,集合里的Region可以是任意年代的。在GC的时候,对于old->young和old->old的跨代对象引用,只要扫描对应的CSet中的RSet即可。 逻辑上说每个Region都有一个RSet,RSet记录了其他Region中的对象引用本Region中对象的关系,属于points-into结构(谁引用了我的对象)。而Card Table则是一种points-out(我引用了谁的对象)的结构,每个Card 覆盖一定范围的Heap(一般为512Bytes)。G1的RSet是在Card Table的基础上实现的:每个Region会记录下别的Region有指向自己的指针,并标记这些指针分别在哪些Card的范围内。 这个RSet其实是一个Hash Table,Key是别的Region的起始地址,Value是一个集合,里面的元素是Card Table的Index。
上图中有三个Region,每个Region被分成了多个Card,在不同Region中的Card会相互引用,Region1中的Card中的对象引用了Region2中的Card中的对象,蓝色实线表示的就是points-out的关系,而在Region2的RSet中,记录了Region1的Card,即红色虚线表示的关系,这就是points-into。
RSet究竟是怎么辅助GC的呢?在做YGC的时候,只需要选定young generation region的RSet作为根集,这些RSet记录了old->young的跨代引用,避免了扫描整个old generation。 而mixed gc的时候,old generation中记录了old->old的RSet,young->old的引用由扫描全部young generation region得到,这样也不用扫描全部old generation region。所以RSet的引入大大减少了GC的工作量。
其实跨代引用的问题一直存在,只是书上没说这个问题,而是说了 CMS、G1 如何高效解决的
3、GC 过程
G1提供了两种GC模式,Young GC 和 Mixed GC,两种都是完全Stop The World的。
Young GC:选定所有年轻代里的Region。通过控制年轻代的 region 个数,即年轻代内存大小,来控制young GC 的时间开销。
Mixed GC:选定所有年轻代里的Region,外加根据 global concurrent marking 统计得出收集收益高的若干老年代 Region。在用户指定的开销目标范围内尽可能选择收益高的老年代 Region。
由上面的描述可知,Mixed GC 不是 full GC,它只能回收部分老年代的 Region,如果 mixed GC 实在无法跟上程序分配内存的速度,导致老年代填满无法继续进行 Mixed GC,就会使用 serial old GC(full GC)来收集整个 GC heap。所以我们可以知道,G1 是不提供 full GC的。
G1 gc 的 过程分为四个步骤:
- 初始标记(Initial Marking):仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS 指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。这个阶段需要 停顿线程,但耗时很短,而且是借用进行Minor GC的时候同步完成的,所以G1收集器在这个阶段实际 并没有额外的停顿。
- 并发标记(Concurrent Marking):从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆 里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以 后,还要重新处理SATB记录下的在并发时有引用变动的对象。
- 最终标记(Final Marking):对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留 下来的最后那少量的SATB记录。
- 筛选回收(Live Data Counting and Evacuation):负责更新Region的统计数据,对各个Region的回 收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region 构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧 Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行 完成的。
4、参考资料
https://tech.meituan.com/2016/09/23/g1.html
https://segmentfault.com/a/1190000039411521
网友评论