垃圾回收器—G1(Garbage First)
G1简介
Jdk7正式引入G1,jdk9及以后默认的垃圾回收器。下图是简单的G1示意图:
- 如上图所示,G1收集器将整个Java堆默认划分为2048个大小相同的独立region块,每个region的大小控制在1MB-32MB之间,必须是整数,可通过参数设定。虽然任然保留了年轻代和老年代的概念,但是新生代和老年代不再是物理隔离了,特们都是由一部分不连续的region组成。
- 上图有色方块有各自所属的区域,s代表survivor区;白色方块表示还未使用的堆空间,GC时使用复制算法用得上或者新对象进来存储用得上。蓝色的humongous主要用于存储大对象,超过了1.5个region就存储在该区域。
- 当一个H区存不下一个大对象时,G1会寻找连续的H区来存储,为了能够找到连续的H区有时候不得不启动Full GC。
G1的特点
- 是一个并行回收器,将堆分割为多个不相关的区域(region,各个region物理上不连续),使用不同的Regin表示Eden、survivor、老年代等。
- G1的GC有计划的避免整个Java堆进行GC,G1跟踪各个region里的垃圾堆积的价值大小(回收获得的空间大小以及回收所需的时间经验值),在后台维护一个优先列表,每次根据允许收集的时间下,优先回收价值最大的region。所以名为垃圾优先(garbage first)。
- G1主要针对配备多核CPU及大容量的机器,在高配置的硬件情况下,以极高概率满足停顿时间的同时,还兼具高吞吐量的性能特征。
G1的优势
- 并行与并发
- 并行性:G1在回收期间,可以多个GC线程并行工作,有效利用多核计算能力。此时用户线程STW。
- 并发性:G1拥有与应用程序交替执行的能力,部分工作可以和应用程序并发执行,因此,一般不会在整个回收阶段发生完全阻塞应用程序的情况。老年代的标记过程就有一个并发标记,此时和用户线程并发执行。
- 分代收集
- 将堆空间分为若干区域(region),这些区域包含了逻辑上的年轻代和老年代。
- 同时兼顾年轻代和老年代的收集。
G1回收算法
G1内存回收是以region作为基本单位的,region是复制算法(region之间进行复制,复制到空闲的region),避免了产生碎片,无需额外维护空闲表。当Java堆非常大的时候,G1优势更明显,随着硬件提升使用G1效率更好。
可预测的停顿时间模型
- 这是G1相对GMS的一大优势,G1建立了一个可预测的停顿时间模型,使用者可指定一个停顿时间长度,G1尽量保持在该时间内完成GC。当然这是软实时策略,也就是说G1会尽量在限定时间内完成但不是一定。
- 分区的原因,G1可以选择对部分region进行回收,缩小回收范围有利于降低停顿时间。
- G1跟踪各个region里的垃圾堆积的价值大小(回收获得的空间大小以及回收所需的时间经验值),在后台维护一个优先列表,每次根据允许收集的时间下,优先回收价值最大的region。保证了G1在限定时间内达到最优收集效率。
G1的缺点
相较于CMS,G1还不具备全方位压倒性优势,比如在用户程序运行过程中,G1无论是为了垃圾收集产生的内存占用还是垃圾收集时的额外负载都比CMS高,所以G1适合用在高配置服务器上。而低配服务器上CMS大概率优于G1。
G1垃圾收集过程
年轻代GC
- 当Eden区用尽时开始年轻代回收过程,G1年轻代回收是一个并行的独占式收集器,G1 在年轻代GC会暂停所有应用程序线程,启动多线程执行执行年轻代GC,然后将存活的对象从年轻代移动至survivor区或老年代。
老年代GC
-
当堆内存使用达到一定阈值(默认45%,可参数设置),开始老年代并发标记过程。
-
初始标记阶段:标记根节点可直达的对象,触发STW,时间很短,在该过程中也在并发进行着young GC。
-
并发标记:在整个堆中进行并发标记(和用户程序并发执行),此过程可能被young GC中断,若region中被标记出全是垃圾则立即回收。若不是则并发标记过程中计算每个区域的对象活性(存活对象比例,后续回收会比例不同优先级不同)。并发标记类似CMS满足了低延迟。(若并发标记过程内存被打满了会触发Full GC)
-
再次标记:修正并发标记的结果,触发STW以免再次产生垃圾导致标记不准确。
-
优先级排序(独占):对各个区域进行优先级排序,识别出可以混合回收的region。
-
老年代并发标记完马上开始一个混合回收,对于一个混合回收期,G1 GC回收过的老年代region幸存的对象利用复制算法移动到其他空闲region,这些region成为老年代的一部分;G1回收器回收老年代和其他老年代回收器不同,G1不需要每次回收整个老年代,而是一次扫描/回收一部分老年代region(因为是根据停顿时间选择符合条件且价值高的的region回收)。同时这些老年代region是和年轻代一起被回收的。
G1的优化建议
-
年轻代大小
若无特殊情况避免使用-Xmn或-XX:NewRatio等相关参数显示设置年轻代的大小。让其动态自适应以达到高效率。 -
暂停时间目标不要设置太苛刻
- G1的吞吐量目标是90%的应用程序时间和10%的垃圾回收时间。
- 太苛刻导致每次GC回收的价值不大,那么GC就会比较频繁,导致吞吐量下降;若每次回收很少也可能导致内存很快被打满造成Full GC。