本文对垃圾回收和G1中容易误解的概念和难以理解的概念作阐述,解决新手学习GC的障碍。
一:关于垃圾回收器中容易误解的概念:
1.并行和并发
[1]:在线程的语境下(上下文中):
并行:同一时间点,有多个任务被执行。
并发:同一时间段,有多个任务被执行,同一时间点上,只有一个任务在执行。
[2]:在垃圾回收的语境下(上下文中):
并行:在用户线程处于等待状态(STW)时,有多个垃圾回收线程并行(线程语境下)工作。
串行:在用户线程处于等待状态(STW)时,只有一个垃圾回收线程工作
并发:用户线程与垃圾回收线程并行(线程语境下)工作
注:所有的垃圾回收器都有STW(Stop The World),所以即便是并发的垃圾回收器,也一定会有STW。
记忆集(Remember Set):每个Region(G1的分区)都有一个记忆集,每次对引用类型变量赋值时,都会产生一个Write Barrier(写屏障),判断引用指向的对象是否和该引用在同一个Region,如果不同,在CardTable中把相关引用信息记录到该引用所指向的对象所在Region的记忆集中。如果卡表中的信息发生更改,比如重新给引用赋值,此时card被标记为脏,进入脏卡表队列,等待YGC时进行操作。
G1的分区(Region):有Eden,Survivor,Old,Humongous(超大对象)区,还有很多空区(毕竟是复制算法),所有的区在清空后,区的属性可以重新改变(当然也可以不变)。比如原来是S区,清空后可以作为O区用。空闲的区域也可以作为任意一种区使用。
Humongous(超大对象)区:大家思考一下,为什么G1要设计一个超大对象区而不是直接把对象丢到老年代?
超大对象比较容易因为Survivor区空间不足而直接晋升至老年代,但是老年代GC的频率极低(相较于YGC),那么如果大对象不再被引用(变成垃圾),并长时间存放在老年代,某种程度上也算是"内存泄漏",所以设计了超大对象区便于回收.
二:垃圾回收过程:
G1垃圾回收器的过程:YGC->YGC+并发标记->混合回收。
1.G1的年轻代回收器是一个并行(垃圾回收语境下)的独占式(STW)垃圾回收器,当堆内存使用空间达到一定值(默认为45%)时,才触发并发标记过程,这个阈值可以自己设置。
2.首先G1创建回收集,回收集指的是需要被回收的内存分段的集合,通常包含整个Survivor区和Eden区。
3.开始YGC:
(1):扫描GC Roots(比如static变量)和记忆集中的引用,作为扫描的根节点。
(2):更新Rset(记忆集),通过脏卡表队列(dirty card queue)中的信息更新记忆集。
大家不妨思考下,为什么要有一个脏卡表队列?
因为如果用同步线程在每次赋值操作时更新记忆集,开销会很大,用队列在STW时更新,不会占用用户线程,开销会小很多。
(3):处理Rset:识别被老年代指向的Eden区中的对象,将对象标记为存活
(4):复制对象:Eden区和from区(Survivor的from区的简称),的对象被复制到to区,对象年龄+1,达到年龄阈值的对象会晋升到老年代。如果对象过大,survivor区放不下,则直接晋升到老年代。
(5): 处理引用(上面四步回收的是强引用对象),接下来回收weak,soft,phantom,final引用对象,这类对象占比极少(1%左右),通常也就框架底层源码中能看到。
--------------------------------------------------------------------------------------------------
并发标记阶段(堆内存占用达到阈值):
[1]:标记从根节点(和上面一样,包括记忆集扫描)直接可达的对象,扫描过程中STW,同时触发YGC
[2]:根区域扫描:扫描从survivor区直接可达的老年代对象,并标记。这个过程必须在[1]的YGC之前,因为YGC会移动Survivor区的Region。
[3]:并发标记(垃圾回收语境下的并发):可以与应用程序并行(线程语境下)执行,标记空间内完全(100%)是垃圾的Region。 计算每个Region中存活对象的比例。
[4]再次标记:停止应用程序并修正上一次的标记结果。
[5] 独占清理: STW,识别各个Region的存活对象并统计GC回收的收益,进行优先级排列,同时识别可以混合回收的区域。注意,这个阶段没有回收任何垃圾。
[6]并发清理:清理在[3]中被标记为完全是垃圾的Region。
------------------------------------------------------------------------------------------------------------------
混合回收阶段:
简单来讲就是每次进行Mixed GC时对Eden区,Survivor区和部分Old区进行GC。
混合回收的回收集包括Eden,Survivor和八分之一的Old区。
混合回收的算法和YGC一样,只不过多了一部分老年代区域。
两点注意事项:
1.垃圾比例低于一定阈值的Region不会被回收。
2.回收集内的垃圾低于堆内存的10%,不会进行此次回收。