目前主流垃圾回收算法(jdk9默认),为运行需要大堆且 GC 延迟有限的应用程序的用户提供解决方案,这意味着大约 6 GB 或更大的堆大小,以及低于 0.5 秒的稳定且可预测的暂停时间。回收步骤与CMS大体相同
G1/CMS区别:
1、 G1 是一个复制压缩收集器
2、G1 提供比 CMS 收集器更可预测的垃圾收集暂停,并允许用户指定所需的暂停目标
优点:使用简单,停顿时长可控制在500毫秒内,内存在大几个G以上均可使用
缺点:垃圾回收总时长较长,不适用于高吞吐量场景
G1保留了年轻代和老年代的概念,但不再是物理隔阂了,它们都是(可以不连续)Region的集合。
G1有专门分配大对象的Region叫Humongous区,大对象的判定规则就是一个大对象超过了一个Region大小的50%,而且一个大对象如果太大,可能会横跨多个Region来存放。所有大对象都被分配为老年代中的一系列连续区域。对象本身的起点始终位于该序列中第一个区域的起点。序列的最后一个区域中的任何剩余空间都将丢失,不能再用于分配。直到整个对象被回收才可再次分配,大对象回收只能在清理暂停期间标记结束时,或者无法访问时在Full GC 期间进行。
对于原始类型数组(例如 bool、各种int和float)的大对象有一个特殊规定。如果大对象在任何类型的垃圾回收暂停时没有被许多对象引用,G1 会尝试回收大对象。此行为默认开启,可通过 -XX:G1EagerReclaimHumongousObjects 禁用。大对象从不移动,即使在 Full GC 期间也如此。这可能导致过早的 Full GC 或由于碎片而导致虽有大量可用空间缺导致内存不足无法分配的情况。
在高层次上,G1 收集器在两个阶段之间交替。 Young-only 阶段包含垃圾收集,这些垃圾收集会逐渐用老年代中的对象填满当前可用的内存。 space-reclamation (空间回收)阶段是 G1 除了处理年轻代外,还逐步回收老年代中的空间。 然后循环以 Young-only 阶段重新开始。
- Young-only当老年代占用率达到-XX:InitiatingHeapOccupancyPercent设置的值时young-only 阶段和Space-reclamation 阶段之间的转换开始
Concurrent Start
Remark
Cleanup - Space-reclamation
G1 GC 通过将活动对象从一组或多组区域(称为集合集 (CSet))增量并行复制到一个或多个不同的新区域以实现压缩来减少堆碎片。目标是回收尽可能多的堆空间,从那些包含最多可回收空间的区域开始,同时尝试不超过暂停时间目标(垃圾优先)。 G1 GC 使用独立的记忆集 (RSets) 来跟踪对区域的引用。独立 RSet 支持并行和独立的区域集合,因为只有一个区域的 RSet 必须被扫描以查找对该区域的引用,而不是整个堆。 G1 GC 使用写后屏障来记录对堆的更改并更新 RSet。
G1 在调整 Java 堆大小时遵守标准规则:
使用 -XX:InitialHeapSize 作为最小 Java 堆大小,-XX:MaxHeapSize 作为最大 Java 堆大小,-XX:MinHeapFreeRatio 用于最小可用内存百分比,-XX:MaxHeapFreeRatio 用于调整大小后可用内存的最大百分比。 G1 收集器只考虑在 Remark 和 Full GC 暂停期间调整 Java 堆的大小。此过程可能会向操作系统释放内存或从操作系统分配内存。
G1 总是在正常的年轻收集结束时为下一个 mutator 阶段调整年轻代的大小。这样,G1 可以满足使用 -XX:MaxGCPauseTimeMillis 和 -XX:PauseTimeIntervalMillis 根据对实际暂停时间的长期观察设置的暂停时间目标。它考虑了类似规模的年轻一代撤离需要多长时间。这包括诸如在收集过程中必须复制多少对象以及这些对象的相互连接程度等信息。如果没有其他约束,则 G1 会在 -XX:G1NewSizePercent 和 -XX:G1MaxNewSizePercent 确定满足暂停时间的值之间自适应调整年轻代的大小。或者,-XX:NewSize 结合 -XX:MaxNewSize 可以用来分别设置最小和最大年轻代大小。
仅通过-XX:NewSize 或 -XX:MaxNewSize一个设置项指定年轻代大小后,这将导致暂停时间设置失效。
Only specifying one of these latter options fixes young generation size to exactly the value passed with -XX:NewSize and -XX:MaxNewSize respectively. This disables pause time control.
在空间回收阶段,G1 尝试在一次垃圾收集暂停中最大化老年代回收的空间量。年轻代的大小设置为允许的最小值,通常由 -XX:G1NewSizePercent 确定。
当收集集候选区域中可回收的剩余空间量小于 -XX:G1HeapWastePercent 设置的百分比时,Space-Reclamation 阶段结束。
定期垃圾收集
如果由于应用程序不活动而长时间没有进行垃圾收集,VM 可能会长时间持有大量未使用的内存,这些内存可以在其他地方使用。为避免这种情况,可以使用 -XX:G1PeriodicGCInterval 选项强制 G1 进行常规垃圾收集。此选项确定 G1 考虑执行垃圾回收的最小间隔(毫秒)。如果自任何先前的垃圾收集暂停以来经过了这段时间,并且没有正在进行的并发循环,G1 会触发额外的垃圾收集,并可能产生以下影响:
- 在 Young-Only 阶段:G1 使用 Concurrent Start pause 启动并发标记,或者,如果指定了 -XX:-G1PeriodicGCInvokesConcurrent,则使用 Full GC。
- 在空间回收阶段:G1 继续空间回收阶段,触发适合当前进度的垃圾收集暂停类型。
-XX:G1PeriodicGCSystemLoadThreshold 选项可用于细化是否触发垃圾回收:如果 JVM 主机系统(例如容器)上的 getloadavg() 调用返回的平均一分钟系统负载值高于此值值,不会运行定期垃圾收集。
G1产生FGC如何解决
扩内存
提供cpu性能,加快垃圾回收速度
降低MixedGC触发阈值(+XX:InitiatingHeapOccupancyPercent),使其及早发生(默认45%)。G1的FGC在jdk10之前是串行,之后并行
参数
java -XX:+PrintFlagsFinal -version |grep G1
- -XX:+UseG1GC
- -XX:GCTimeRatio GC时间建议比例,G1会根据该值调整堆空间
- -XX:ConcGCThreads 线程数量
- -XX:ParallelGCThreads 指定GC工作的线程数量
- -XX:G1HeapRegionSize 指定分区大小(必须是2的N次幂,jdk8-17效值范围为 1 到 32MB,jdk18为1-512 MB),默认将整堆划分为2048个分区
- -XX:MaxGCPauseMillis 目标暂停间隔时间(默认200ms),G1会尝试调整young区块数来达到该值
- -XX:G1NewSizePercent 新生代内存初始空间(默认整堆5%)
- -XX:G1MaxNewSizePercent 新生代内存最大空间,默认60%
- -XX:TargetSurvivorRatio Survivor区的填充容量(默认50%),Survivor区域里的一批对象(年龄1+年龄2+年龄n的多个 年龄对象)总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代
- -XX:MaxTenuringThreshold 最大年龄阈值(默认15)
- -XX:InitiatingHeapOccupancyPercent老年代占用空间达到整堆内存阈值(默认45%),则执行新生代和老年代的混合 收集(MixedGC),比如我们之前说的堆默认有2048个region,如果有接近1000个region都是老年代的region,则可能 就要触发MixedGC了
- -XX:G1MixedGCLiveThresholdPercent (默认85%) region中的存活对象低于这个值时才会回收该region,如果超过这 个值,存活对象过多,回收的的意义不大。
- -XX:G1MixedGCCountTarget 在一次回收过程中指定做几次筛选回收(默认8次),在最后一个筛选回收阶段可以回收一 会,然后暂停回收,恢复系统运行,一会再开始回收,这样可以让系统不至于单次停顿时间过长。
- -XX:G1HeapWastePercent (默认5%) gc过程中空出来的region是否充足阈值,在混合回收的时候,对Region回收都 是基于复制算法进行的,都是把要回收的Region里的存活对象放入其他Region,然后这个Region中的垃圾对象全部清 理掉,这样的话在回收过程就会不断空出来新的Region,一旦空闲出来的Region数量达到了堆内存的5%,此时就会立 即停止混合回收,意味着本次混合回收就结束了。