G1垃圾回收器在并发场景调优

童威
• 阅读 2025

一、序言

目前企业级主流使用的Java版本是8,垃圾回收器支持手动修改为G1,G1垃圾回收器是Java 11的默认设置,因此G1垃圾回收器可以用很长时间,现阶段垃圾回收器优化意味着针对G1垃圾回收器优化。

为了简化讨论,下面假设针对4C/16G物理机器进行优化。

二、G1概览

(一)了解G1

1、最大堆大小

G1管理的最大堆大小为64G。每个Region的大小通过-XX:G1HeapRegionSize来设置,大小为1~32MB,默认最多可以有2048个Region,G1能管理的最大堆内存是32MB*2048=64G

使用G1垃圾回收器最小堆内存应为1MB*2048=2GB,低于此值建议使用其它垃圾回收器。

2、Region大小

Region大小为1~32MB,具体取值有1MB、2MB、4MB、8MB、16MB、32MB,Region大小优化与大对象有关,当对象占用内存超过Region的一半时将被视为大对象。

被标记为大对象将不利于垃圾回收。

3、获取默认值

查看本地JVM特别是G1垃圾回收器当前的默认值。

java -XX:+PrintFlagsInitial  >> ~/1.txt

(二)三种GC模式

G1垃圾回收器有两种垃圾回收模式,新生代回收混合回收,特殊情况下会切换到Full GC

1、新生代回收

新生代回收在最大停顿时间内,会处理所有Eden区的垃圾。具体操作是将Eden区所有存活的对象复制到Survivor区,同时清空Eden区。

新生代回收伴随着应用暂停,最长停顿时间不超过最大停顿时间,新生代回收尽管有暂停机制,考虑到并行回收的特性,回收逻辑相对简单,回收效率依然较高。一般而言,新生代回收实际耗时通常低于最大停顿时间。

新生代回收触发时机是新创建的对象在Eden区找不到足够的存储空间。

2、混合回收

混合回收伴随着新生代回收和老年代回收,在最大停顿时间范围内,会处理大部分Eden区的垃圾和一部分老年代垃圾。

老年代回收毫无疑问会伴随着应用暂停。混合回收操作比较复杂,相对新生代回收来说,单位时间回收的垃圾数要少,回收效率要低。一般而言,混合回收的实际耗时通常接近或者等于最大停顿时间。

混合回收触发时机是由参数InitiatingHeapOccupancyPercent控制,默认值为45,含义是老年代占用空间大小与堆的总大小比值超过此数便会触发混合回收。

默认值45%是比较合理的,不建议所谓的调优。老年代回收策略同样是将选定Region区内存活的对象复制到空闲Region区,混合回收伴随着回收新生代垃圾能够清理出更大的空闲Region区来存放老年区存活对象,保证回收过程能够正常进行。

老年区存活对象一般较多,对象在内存中复制耗时较长,因此相对来说混合回收效率较低。

3、Full GC

Full GC是所有G1垃圾回收调优者尽力回避的情况,单线程回收垃圾,回收对象是整个堆,不再受最长停顿时间约束,一旦出现此情况,意味着应用的响应时间无情的变长。

当应用不定期进入Full GC状态时,与其任由其单线程重塑堆内存,不如采用冗余策略,在流量低谷时刻,逐一重启应用,主动重塑堆内存空间。

流量高峰期出现Full GC现象及其应对策略后面再讨论。

(三)默认参数

1、堆内存
参数默认值说明优化建议
MaxGCPauseMillis200ms最大停顿时间
G1HeapRegionSize 不设置时启发式推断
G1NewSizePercent5新生代最小百分比
G1MaxNewSizePercent60新生代最大百分比
2、新生代内存回收
参数默认值说明优化建议
ParallelGCThreads 并行GC线程数,会根据CPU核数推断默认值
MaxTenuringThreshold15从新生代晋升到老年代年龄阈值
SurvivorRatio8Eden和一个Survivor的比例
TargetSurvivorRatio50Survivor区内存使用率,增大该值会降低到老年代概率
+G1EagerReclaimHumongousObjectstrue是否在YGC时回收大对象
3、混合回收
参数默认值说明优化建议
G1MixedGCCountTarget8值越大,收集老年代分区越少
G1OldCSetRegionThresholdPercent10表示一次最多收集10%的分区

三、垃圾在堆中流转

垃圾回收器调优的关键是尽可能减少Mixed GC的频率,换句话说尽可能减少垃圾流转到老年代。GC调优便是认识垃圾在堆中的流转规律,从而对流向老年代的垃圾予以提前干涉,使之尽可能留在新生代

垃圾在新生代(主要指Eden区)中,垃圾回收使用YGC,回收线程与应用线程并发进行,垃圾回收对应用透明进行,假如CPU算力充足的话,应用几乎感觉不到垃圾在回收进行。

垃圾在老年代中,垃圾回收采用Mixed GC,回收线程开始工作时,应用线程阻塞,等待回收线程工作完毕有,应用线程重新被唤醒。频繁的Mixed GC对应用的吞吐量产生不良影响。

1、对象如何进入老年代

一般而言,新创建的对象会存在于新生代的Eden区,下一次垃圾回收处罚便直接回收了。如果对象比较顽强(继续被其它对象引用),那么会在Survivor区流转,每GC一次,仍然不能被垃圾回收,那么年龄加一,继续在S0和S1区流转,当年龄增长到一定的阈值,直接进入老年代。

(1)大对象直接到老年代

新创建的对象如果过大,那么不经过新生代,直接进入老年代。控制对象大小阈值有参数-XX:PretenureSizeThreshold决定,单位字节

(2)动态年龄判断

除了对象在S0和S1区反复流转年龄变化外,垃圾回收维护另外一套独立的年龄判定规则:如果YGC后尚未被回收的垃圾超过了Survivor区的50%,那么超过的这批对象会直接进入老年代。

12G * 60% * 10% * 50% * 1024 = 737MB

动态年龄判定规则要求每次YGC尽可能的彻底,意味着每次GC的最长时间不能太短,默认200毫秒是比较合理的值。

如果预设置的最长停顿时间过短,那么每次GC后存活大量尚未被回收的垃圾,S区容量有限,不该进入老年代的垃圾快速在老年代堆积,频繁的Mixed GC不可避免。

2、高并发加速进入老年代

在高并发场景下,CPU和内存资源吃紧,负载很高,不确定的性能抖动加速垃圾进入老年代。

举例说明,DAO层查询数据库,一次完整的会话结束后,整个会话中产生的对象垃圾在Eden区应当被全部回收。由于网络波动,数据库处理能力的限制,大量会话超时。在此过程中这部分对象垃圾很可能在快速S0和S1流转中叠加年龄,或者触发动态年龄判定,直接进入老年代。

老年代内存空间不够用,触发Mixed GC,Mixed GC直接副作用是应用卡顿。

四、调优步骤

1、设置垃圾回收器

Java 8需要手动指定G1垃圾回收器,命令行添加-XX:+UseG1GC参数。

2、设置堆大小

设置内存堆大小有两点需要注意:初始堆大小与最大堆大小保持一致;堆大小占物理内存大小75%~80%,给系统核心服务预留必要的内存。

参数-Xmx12G设置初始堆大小;参数-Xms12G设置最大堆大小。

3、元空间设置

元空间是指存储静态类、静态方法、常量等特殊变量的内存区域。

参数-XX:MetaspaceSize=1G设置元空间初始大小;参数-XX:MaxMetaspaceSize=1G设置元空间最大大小。

4、GC停顿时间

GC停顿时间是指每次YGC或者Mixed GC的最大时间,垃圾回收器会根据用户设置的期望时间动态选择垃圾扫描的范围,如果设置时间过小,可能总有一部分垃圾不能得到回收。单位毫秒

-XX:MaxGCPauseMillis=200
5、新生代大小

参数-XX:G1NewSizePercent设置新生代初始大小,默认为5%;参数-XX:G1MaxNewSizePercent设置新生代最大大小,默认为60%

新生代内部细化为 Eden区和两个 Survivor ,默认比例是: 8:1:1

Eden:   12G * 60%* 80% = 5.76G
S0:     12G * 60%* 10% = 0.72G
S1:     12G * 60%* 10% = 0.72G

假设并发系统每秒创建500MB的对象,假设每次YGC根据预先设置的最长停顿时间都能够扫描到Eden Region,那么此并发系统大约每隔10秒需要进行一次YGC。

五、调优实践

GC垃圾回收调优是在物理硬件受限制,并且有调优的理论空间下进行的。条件允许的话,直接升级硬件配置特别是物理内存配置,能够有效降低GC频率。比如8C32G或者16C64G等。

1、频繁的YGC

当并发量较大时,频繁的YGC时必然的,单位时间类创建了更多的对象,使用完毕之后成为了垃圾。频繁的YGC有加速S区对象流向老年代的可能,尽可能保证每次YGC的实际耗时低于预设置的最长垃圾回收时间(默认200毫秒),以便能够每次都能将新生代垃圾清理完成,尽可能延缓垃圾流向老年代。

2、频繁的Mixed GC

在G1垃圾回收器中,没有所谓的Mixed GC的概念,Mixed GC类似于F·GC,不同的是Mixed GC除了回收老年代,同时也回收新生代,共同之处在于都会产生STW

频繁的Mixed GC本质是大量应该在新生代回收的垃圾进入了老年代,解决思路是排查哪些哪些垃圾(对象)应该留在新生代,却流转到老年代。

(1)大对象

检查应用程序是否周期性的创建大对象,大对象的阈值由参数-XX:PretenureSizeThreshold控制。假如内存有优化空间的前提下适当调高此值,不得超过S区的一半(似乎没有这么大的对象),副作用是新生代存放对象数量相应变少,Eden区内存更快的用完,YGC相应的变频繁一些。

从业务的角度来讲,大对象产生必有其产生的原因,从这个角度优化可能性不高,垃圾回收器优化尽可能屏蔽业务层代码,毕竟对开发提要求让其不要创建大对象不现实。

(2)元空间

元空间耗尽也会引发Mixed GC,考虑到元空间存储内容的特殊性,因元空间耗尽导致GC频率提高并没有很好的办法。单纯提高元空间大小会压缩新生代大小,新生代变小,对象流转到老年代的数量会变多,老年代内存消耗加快,同样会提高GC的频率。

因元空间耗尽引发的Mixed GC,相对来说增加物理内存是比较优的解决方式。

3、Full GC

尽管Mixed GC被触发时,应用会暂时停止响应(默认值是200毫秒),暂停的时间是相对可控的。

如果在进行Mixed GC时,空闲的Region无法保存存活的对象,Mixed GC无法正常进行时,垃圾回收会切换到 G1 之外的 Serial Old GC 来收集整个堆,包括新生代、老年代、元空间等。

进入Serial Old GC垃圾回收状态,垃圾回收不再受最长回收时间约束,采用单线程进行标记、清理和压缩整理,应用可能进入假死状态。也许重启应用,重新分配堆内存,将堆内存彻底洗牌,也许会更好。

G1垃圾回收调优的关键是不要出现Full GC,因此对于敏感的参数千万不要乱调优,否则不仅达不到理想想过,反而更糟糕。

喜欢本文点个♥️赞♥️支持一下,如有需要,可通过微信dream4s与我联系。相关源码在GitHub,视频讲解在B站,本文收藏在博客天地

点赞
收藏
评论区
推荐文章
从原理聊JVM(一):染色标记和垃圾回收算法
本篇介绍了JVM中垃圾回收器相关的基础知识,后续会深入介绍CMS、G1、ZGC等不同垃圾收集器的运作流程和原理,欢迎关注。
Wesley13 Wesley13
4年前
JAVA之G1垃圾回收器
概述G1GC,全称GarbageFirstGarbageCollector,通过XX:UseG1GC参数来启用,作为体验版随着JDK6u14版本面世,在JDK7u4版本发行时被正式推出,相信熟悉JVM的同学们都不会对它感到陌生。在JDK9中,G1被提议设置为默认垃圾收集器(JEP248)。在官网中,是这样描述G1的:The
Wesley13 Wesley13
4年前
CMS垃圾回收过程
1.总体介绍:CMS(ConcurrentMarkSweep)是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。在启动JVM参数加上\XX:UseConcMarkSweepGC ,这个参数表示对于老年代的回收采用CMS。CMS采用的基础算法是:标记—清除。2.CMS
Wesley13 Wesley13
4年前
Java虚拟机调优系列PDF免费下载
1、目录Java虚拟机调优系列整理了下,导出成pdf了,方便朋友们查看,以下是目录:(1)Java虚拟机调优(一)一些概念(2)Java虚拟机调优(二)一些概念(续)(3)Java虚拟机调优(三)基本垃圾回收算法(4)Java虚拟机调优(四)垃圾回收面临的问题(5)Java虚拟机调优(五)分代垃圾回收详述1(6)Jav
Stella981 Stella981
4年前
JVM系列篇:7种JVM垃圾收集器特点,优劣势、及使用场景
本系列会持续更新。!(https://oscimg.oschina.net/oscnet/945dbe48630eb4284fea936b19161c0f08a.jpg)今天继续JVM的垃圾回收器详解,如果说垃圾收集算法是JVM内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。一、常见的垃圾收集器
Wesley13 Wesley13
4年前
Java G1 GC 垃圾回收深入浅出
1\.G1概览G1GC 全称是GarbageFirstGarbageCollector,垃圾优先垃圾回收器,以下简称G1。G1是HotSpotJVM的短停顿垃圾回收器。其实关于G1的论文早在2004年就有了,但是G1是在2012年4月发布的JDK7u4中才实现。从长期来说,G1旨在取代CMS(ConcurrentMark
Wesley13 Wesley13
4年前
Java虚拟机垃圾回收相关知识点全梳理(上)
一、前言笔者最近在复习JVM的知识,本着记录分享的精神,整理下学习Java虚拟机垃圾回收相关知识点,由于整个垃圾回收内容比较多,我将整理成上下两篇文章去分享,上篇我会主要分享Java虚拟机的运行时数据区域划分,垃圾回收算法。下篇文章主要分享Java虚拟机的垃圾回收器以及一些虚拟机调优建议。二、运行时数据区Java虚拟机
Stella981 Stella981
4年前
JVM垃圾回收器思维导图
JVM垃圾回收器思维导图,介绍了各种垃圾回收器概述,垃圾收集的算法及其特点,使用场景!(https://oscimg.oschina.net/oscnet/2580cd2986314278b730349543a2bdbe.png)思维导图下载文件:(包含上次java线程)地址:https://pan.baidu.com/s/1nv
Wesley13 Wesley13
4年前
JVM垃圾回收器
1,先贴上4中回收器的参数:\XX:UseSerialGC串行垃圾回收器\XX:UseParallelGC并行垃圾回收器\XX:UseConcMarkSweepGC并发标记扫描垃圾回收器\XX:UseG1GCG1垃圾回收器2,分别测试每种参数1默认的情况是(XX:UseSerialGC),即使不
Stella981 Stella981
4年前
JVM架构体系与GC命令小总结
!(https://oscimg.oschina.net/oscnet/052a011b9ad19376d76daa6b6dcb82fa032.png)1.Overview2.JVM架构体系1)垃圾回收对象存活性判断垃圾回收算法垃圾回收器(回收算法的具体实现)
G1垃圾回收参数调优及MySQL虚引用造成GC时间过长分析 | 京东云技术团队
我方有一应用,偶尔会出现GC时间过长(间隔约4小时),导致性能波动的问题(接口最长需要耗时3秒以上)。经排查为G1垃圾回收器参数配置不当叠加MySQL链接超过闲置时间回收,产生大量的虚引用,导致G1在执行老年代混合GC,标记阶段耗时过长导致。以下为对此问题的分析及问题总结。
童威
童威
Lv1
春色边城动,客思故乡来。
文章
4
粉丝
0
获赞
0