Garbage First收集器
-
G1是JDK9及以上版本中服务器端模式下的默认垃圾收集器
-
G1可以面向堆内存任何部分来组成回收集(Collection Set)进行回收,衡量标准不再是踏属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大,这就是G1的Mixed GC模式
-
G1把堆分成多个大小相等的独立区域(Region),每个Region根据需要扮演Eden、Survivor、老年代空间。每个Region的大小可以通过-XX:G1HeapRegionSize设定,范围是1MB-32MB,且为2的N次幂
-
Region中还有一类特殊的Humongous区域,专门用来存储大对象。G1认为只要大小超过了一个Region容量一般的对象即可判定为大对象。对于超过了整个Region容量的超级大对象,将会被存放在N个连续的Humongous Region之中,G1大多数行为都把Humongous Region作为老年代的一部分看待
-
G1收集器去跟踪各个Region里面的垃圾堆积的“价值”大小,价值即回收所获得的空间大小以及回收所需时间的经验值,然后在后台维护一个优先级列表,每次根据用户设定允许的收集停顿时间(默认200毫秒)优先处理回收价值收益最大的那些Region
-
G1至少要耗费大约相当于Java堆容量10%~20%的额外内存来维持收集器工作
-
G1在整体上采用标记-整理算法,在Region之间采用标记-复制算法,所以G1运作期间不会产生内存空间碎片。
-
跨Region引用对象问题
-
每个Region都维护有自己的记忆集,这些记忆集会记录下别的Region指向自己的指针,并标记这些指针分别在哪些卡片的范围之内。
-
G1记忆集在存储结构上本质是一种哈希表,Key是别的Region的起始地址,Value是一个集合,里面存储的元素是卡表的索引号
-
-
并发标记阶段的实现
-
G1通过原始快照算法(SATB)解决用户线程改变对象引用时,不打破原本对象图结构的问题
-
G1为每个Region设计了两个名为TAMS(Top at Mark Start)的指针,把Region中的一部分空间划分出来用于并发回收过程中的新对象分配,并发回收时新分配的对象地址都必须要在这两个指针位置上。G1收集器默认在这个地址以上的对象是被隐式标记过的,即默认他们是存活的,不纳入回收范围内。
-
如果内存回收的速度赶不上内存分配的速度,G1收集器也要*冻结用户线程执行,导致Full GC而产生长时间的“STOP THE WORLD”
-
-
建立可靠的停顿预测模型问题
-
G1收集器的停顿预测模型是以衰减均值为理论基础实现的,在垃圾收集过程中,G1收集器会记录每个Region的回收耗时,每个Region记忆集里的脏卡数量等各个可测量的步骤花费的成本,并分析得出平均值、标准偏差、置信度等统计信息。
-
衰减平均值会比普通的平均值更容易受到新数据的影响。Region的统计状态越新越能决定其回收的价值。
-
G1收集器的收集过程
-
初始标记:仅仅标记GC Roots能直接关联到的对象,并且修改TAMS指针的值。让下一阶段并发标记过程中,用户线程可以正确的在可用的Region中分配新对象。这个过程需要停顿线程,但是耗时很短,而且是借用Minor GC的时候同步完成的。
-
并发标记:从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆中的对象图,找出要回收的对象,耗时较长,和用户进程并发执行。
-
最终标记:对用户线程做一个短暂的暂停,用于处理并发阶段结束后遗留下来的最后那少量的SATB记录
-
筛选回收:更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以*选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。必须暂停用户线程,由多条收集线程并行完成