G1回收器
这一篇是详细说明G1回收器的,因为他相对来说确实是个缝合怪,上篇的内容又太多了所不清楚,所有这一篇进行详细的说明,
第一个呢就是其实G1是兼顾并行和并发的,简单来说就是既可以并行也可以并发,最大程度利用系统资源,在实现可达的最大吞吐量的前提下,尽可能的减少STW的时间,简单的流程就是执行GC会先STW,然后多线程执行标记或者回收实现并行,然后切换回用户线程,实现并发。
第二个就是分代分区的不同,上一篇主包有说过G1使用了分区算法把内存分成小的区域Region,但是分代其实和其他的回收器是不同的,他没有像其他区一样很严格的进行年轻代和老年代的划分,如图:
他是先分区再分代的,也就是年轻代和老年代的区域可以不连续的,那同学就会问怎么执行标记整理?老年代不得乱套啊,其实不会的还是和之前一样向起始地址移动。
第三种就是可预测的时间模型,因为采用了分区算法和分代算法,让年轻代和老年代的区域更加的小了,G1可以根据各个需要回收的Region来进行价值排序任何放入列表优先回收(回收的空间和回收需要的时间)。这个就比较厉害了可以在相同的时间内比其他的回收器回收的效率更高,因为可以在不同的时间里选取最优的区域进行回收。
缺点就是内存的占用是比其他任何回收器都要高的多,这也是没办法要维护的表和屏障就比其他的多多了,因为是缝合怪的缘故所以推荐使用的内存就是6GB+使用G1。
G1回收器的参数设置
基础参数:
- **
-XX:+UseG1GC
**:启用G1垃圾回收器(必备参数)。 - **
-XX:MaxGCPauseMillis
:设置目标最大停顿时间(默认200ms)。G1会尝试调整堆分区(Region)大小和回收策略以达到该目标,但不保证严格满足**。这个和上面说的可预测时间模型是有关系的,如果设置的太短那么回收Region选择的就会很少,如果设置的太大又会让用户觉得卡顿。 - **
-XX:GCPauseTimeInterval
**:设置GC停顿的间隔时间目标(毫秒),与MaxGCPauseMillis
配合使用。字面意思就是下一次GC相隔的时间。
堆与Region配置:
- **
-XX:G1HeapRegionSize
**:设置Region大小(1MB~32MB,需为2的幂)。默认值由堆大小自动计算(所以到Region都是相同的,设置了后直到程序结束这个过程中Region的空间是不会改变的,另外G1回收器还有个Humongous区域就是存大对象的,超过1.5个Region就放在Humongous中,而这个H区是连续的不像R一样分成很细的区域,不然一个大对象岂不是没地方放了),大Region可减少碎片但可能增加停顿时间,小的话就是相反咯增加内存碎片但是减少停顿时间。 - **
-XX:InitiatingHeapOccupancyPercent
(IHOP)**:触发并发标记周期的堆占用阈值(默认45%)。当内存占用超过此比例时,启动混合GC。 - **
-XX:G1ReservePercent
**:保留堆空间的百分比(默认10%),用于晋升失败时的回退,避免Full GC。
GC触发与并行度:
- **
-XX:ConcGCThreads
**:并发标记阶段的线程数(默认值基于CPU核心数)。过多线程可能影响应用吞吐量。 - **
-XX:ParallelGCThreads
**:STW阶段(如年轻代GC)的并行线程数,默认与CPU核心数相关。 - **
-XX:G1MixedGCCountTarget
**:混合GC的预期回收次数(默认8次),控制老年代Region的回收节奏。 - **
-XX:G1HeapWastePercent
**:允许的堆浪费比例(默认5%)。当可回收空间低于此值时,停止混合GC。
还有很多的命令这边就不一个一个去解释介绍了,有兴趣的小伙伴可以JVM实战-G1参数调优 - 鱼007 - 博客园自行查看了解。但是G1的设计初衷就是为了简化JVM的调优,一般的调整只需要三步,启用G1回收器,设置堆的最大内存,设置最大时间。
回收器和回收模式
不知道小伙伴还记不记得主包之前有提过的什么MinorGC、MajorGC、FullGC呢(JVM学习日记(二)Day2-CSDN博客)?那他们和主包上一篇讲的7大GC有什么关系呢?
其实很好理解他们的关系就是回收器和回收模式,像MinorGC指的就是回收年轻代而Serial指的就是回收器,也就是MinorGC是所以年轻代GC的代名词,而实际怎么去实现GC用什么算法是GC回收器决定的(这里提一下还有个MixedGC也就是混合GC,也是G1可以使用的,简单来说就是同时回收年轻代和老年代,但是这个是部分回收,不像FullGC全部回收)。
记忆集与写屏障
记忆集(Remembered Set, RSet)
每个Heap Region(内存分区)都有一个RSet,用于记录其他Region中的对象对本Region的引用。例如:老年代Region A中的对象引用了年轻代Region B中的对象,则Region B的RSet会记录这个引用关系(Rset记录的是谁引用了我,而不是我引用了谁)。为什么需要RSet?G1是基于Region的分区回收器,回收时需要知道哪些Region是存活的(避免误回收)。如果没有RSet,每次回收一个Region时,需要扫描整个堆才能确定是否有其他Region引用它,效率极低。RSet让G1可以只扫描相关Region,大幅减少GC工作量。sRSet如何实现?通常是一个哈希表或卡表(Card Table)结构,存储引用来源的Region或卡页(Card)信息。例如:G1使用“卡表”(Card Table)来粗粒度记录跨Region引用,再通过RSet细化管理。那什么又是卡表?将内存划分为固定大小的卡页,每个卡页对于卡表中的一个标记位,简单来说就是RegionA引用了RegionB中某一块内存的对象,RegionA的Rset就会记录这个被引用内存的卡页。
写屏障(Write Barrier)
在对象引用被修改时触发,记录跨Region引用的变化到RSet。例如:当对象A(在Region 1)的字段被修改,指向对象B(在Region 2)时,写屏障会更新Region 2的RSet。为什么需要写屏障?在并发标记阶段,用户线程可能修改对象引用关系,导致标记结果不准确(漏标或误标)。写屏障通过捕获引用变化,保证RSet和标记结果的正确性。写屏障也分前/后写屏障,简单来说就是修改被引用对象的前/后修改引用他的Rset。
结构大概就是这样子,这个的外R地址指的是外部Region的地址哦,这个ABCDE就是这个Region中存放的区块。
外部Region地址 | 脏卡页索引 | 实际引用位置 |
---|---|---|
0x10000000 | Bitmap: ...0100 | Region 1的卡页2(0x10001000) |
0x30000000 | Bitmap: ...100000 | Region 3的卡页5(0x30002800) |
G1回收器的回收阶段
1. 年轻代GC(Young GC)
触发条件:Eden区Region占满时触发(STW)。快速回收年轻代(Eden + Survivor)。关键步骤:
- 初始标记(Initial Mark):短暂STW,标记GC Roots直接可达的对象(借Young GC执行)。
- 根区域扫描(Root Region Scanning):扫描Survivor区中引用老年代的对象(需在下次Young GC前完成)。
- 复制存活对象(Evacuation):并行将存活对象复制到Survivor区或晋升到老年代。
- 调整Region分配:根据
MaxGCPauseMillis
动态调整Eden/Survivor的Region数量。
2. 并发标记周期(Concurrent Marking Cycle)
- 触发条件:老年代占用达到
InitiatingHeapOccupancyPercent
(默认45%)。标记全堆存活对象,为混合GC做准备。关键步骤:
- 初始标记(Initial Mark):短暂STW,标记GC Roots直接可达的对象(与Young GC共用)也就是先大概全堆标记一下确定范围。
- 并发标记(Concurrent Marking):短暂STW与用户线程并发遍历堆,标记存活对象。
- 最终标记(Remark):短暂STW,处理并发标记期间遗漏的对象,可能上个阶段并发的时候用户线程又改变了部分对象,可能出现漏标。
- 清理(Cleanup):统计Region存活数据,识别可回收Region(部分STW)。
3. 混合回收(Mixed GC)
- 触发条件:并发标记完成后启动。回收年轻代 + 部分老年代Region(存活率低的Region优先)。关键步骤:
- 选择回收集(Collection Set):包含所有年轻代Region + 部分老年代Region(基于
G1MixedGCLiveThresholdPercent,也就是对象存活率,默认65%当一个Region中存活的对象的内存占用小于这个Region的65%,这个区域会被优先收回
)。 - 复制存活对象(Evacuation):并行将存活对象复制到空闲Region(STW)。
- 调整策略:根据
G1HeapWastePercent(允许可浪费空间,当可回收的垃圾小于这个默认值就不好采用混合GC)
决定是否继续混合回收。
4.FULLGC
这个就不多说啦,就是内存不足的时候无法使用上面的GC了就会触发这个最后的GC,退回到单线程的Serial Old GC(完全STW,长时间停顿)。
总结
本篇主要讲的就是G1回收器,本篇内容比较多,大火们慢慢品哦。