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

目录
  • 序言
  • G1概览
    • 1、最大堆大小
    • 2、Region大小
    • 3、获取默认值
  • 三种GC模式
    • 1、新生代回收
    • 2、混合回收
    • 3、Full GC
  • 默认参数
    • 1、堆内存
    • 2、新生代内存回收
    • 3、混合回收
  • 垃圾在堆中流转
    • 1、对象如何进入老年代
      • (1)大对象直接到老年代
      • (2)动态年龄判断
    • 2、高并发加速进入老年代
  • 调优步骤
    • 1、设置垃圾回收器
    • 2、设置堆大小
    • 3、元空间设置
    • 4、GC停顿时间
    • 5、新生代大小
  • 调优实践
    • 1、频繁的YGC
    • 2、频繁的Mixed GC
      • (1)大对象
      • (2)元空间
    • 3、Full GC

序言

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

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

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、堆内存

参数 默认值 说明 优化建议
MaxGCPauseMillis 200ms 最大停顿时间  
G1HeapRegionSize   不设置时启发式推断  
G1NewSizePercent 5 新生代最小百分比  
G1MaxNewSizePercent 60 新生代最大百分比  

2、新生代内存回收

参数 默认值 说明 优化建议
ParallelGCThreads   并行GC线程数,会根据CPU核数推断 默认值
MaxTenuringThreshold 15 从新生代晋升到老年代年龄阈值  
SurvivorRatio 8 Eden和一个Survivor的比例  
TargetSurvivorRatio 50 Survivor区内存使用率,增大该值会降低到老年代概率  
+G1EagerReclaimHumongousObjects true 是否在YGC时回收大对象  

3、混合回收

参数 默认值 说明 优化建议
G1MixedGCCountTarget 8 值越大,收集老年代分区越少  
G1OldCSetRegionThresholdPercent 10 表示一次最多收集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,因此对于敏感的参数千万不要乱调优,否则不仅达不到理想想过,反而更糟糕。

以上就是G1垃圾回收器在并发场景调优详解的详细内容,更多关于G1垃圾回收器并发调优的资料请关注我们其它相关文章!

(0)

相关推荐

  • java 垃圾回收机制以及经典垃圾回收器详解

    判断对象存活方法 引用计数法:在对象中添加一个引用计数子,每当一个地方引用他时,计数器就加一,当引用失效时,计数器就减一. 会有对象循环引用问题: objA.instance = objB objB.instance = objA objA 有objB 的引用 objB 有 objA 的引用,他们相互引用着对方.导致他们无法回收. 可达性分析: 从GC Roots 根对象作为起点,根据引用关系向下搜索,如果对象可达,就说明对象存活,如果对象不可达,就说明对象可以被回收. GC Roots的根对象

  • JVM的垃圾回收算法一起来看看

    目录 垃圾回收算法 概念 1.标记算法 1.1引用计数法(ReferenceCounting) 1.2可达性分析算法(ReachableAnalysis) 2.回收算法 2.1标记清除算法(MarkSweep) 2.2复制算法(Copying) 2.3标记压缩算法(Mark-Compact) 2.4分代回收算法 总结 垃圾回收算法 概念 垃圾回收(Garbage Collection,GC).程序的运行需要资源,无效的对象如果不及时清理就会一直占用资源,所以对内存资源管理就变得十分重要.而Jav

  • Java 中的垃圾回收机制详解

    目录 介绍 重要条款: 使对象符合 GC 条件的方法 请求JVM运行垃圾收集器的方式 定稿 总结 介绍 在 C/C++ 中,程序员负责对象的创建和销毁.通常程序员会忽略无用对象的销毁.由于这种疏忽,在某些时候,为了创建新对象,可能没有足够的内存可用,整个程序将异常终止,导致OutOfMemoryErrors. 但是在 Java 中,程序员不需要关心所有不再使用的对象.垃圾回收机制自动销毁这些对象. 垃圾回收机制是守护线程的最佳示例,因为它始终在后台运行. 垃圾回收机制的主要目标是通过销毁无法访问

  • java垃圾回收之实现串行GC算法

    我们可以选择JVM内置的各种算法.如果不通过参数明确指定垃圾收集算法, 则会使用宿主平台的默认实现.本章会详细介绍各种算法的实现原理. 下面是关于Java 8中各种组合的垃圾收集器概要列表,对于之前的Java版本来说,可用组合会有一些不同: Young Tenured JVM options Incremental(增量GC) Incremental -Xincgc Serial Serial -XX:+UseSerialGC Parallel Scavenge Serial -XX:+UseP

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

    目录 序言 G1概览 1.最大堆大小 2.Region大小 3.获取默认值 三种GC模式 1.新生代回收 2.混合回收 3.Full GC 默认参数 1.堆内存 2.新生代内存回收 3.混合回收 垃圾在堆中流转 1.对象如何进入老年代 (1)大对象直接到老年代 (2)动态年龄判断 2.高并发加速进入老年代 调优步骤 1.设置垃圾回收器 2.设置堆大小 3.元空间设置 4.GC停顿时间 5.新生代大小 调优实践 1.频繁的YGC 2.频繁的Mixed GC (1)大对象 (2)元空间 3.Full

  • java虚拟机之JVM调优详解

    JVM常用命令行参数 1. 查看参数列表 虚拟机参数分为基本和扩展两类,在命令行中输入 JAVA_HOME\bin\java就可得到基本参数列表. 在命令行输入 JAVA_HOME\bin\java –X就可得到扩展参数列表. 2. 基本参数说明: -client,-server: 两种Java虚拟机启动方式,client模式启动比较快,但是性能和内存管理相对较差,server模式启动比较慢,但是运行性能比较高,windos上采用的是client模式,Linux采用server模式 -class

  • Java JVM虚拟机调优详解

    目录 jmap查看内存信息 jstack jinfo查看jvm系统参数 Jstat查看堆内存使用和类加载的数量信息 内存泄漏 jmap查看内存信息 jmap histo /pid > ./log.txt :查看某一进程实例个数,占用内存的字节数,以及所属的类 jmap -heap /pid :查看堆信息 jmap ‐dump:format=b,file=app.hprof /pid 通过jvisualvm命令启动jvm可视化管理界面可导入dump文件进行分析:查看类的实例 jstack 分析死锁

  • python机器学习朴素贝叶斯算法及模型的选择和调优详解

    目录 一.概率知识基础 1.概率 2.联合概率 3.条件概率 二.朴素贝叶斯 1.朴素贝叶斯计算方式 2.拉普拉斯平滑 3.朴素贝叶斯API 三.朴素贝叶斯算法案例 1.案例概述 2.数据获取 3.数据处理 4.算法流程 5.注意事项 四.分类模型的评估 1.混淆矩阵 2.评估模型API 3.模型选择与调优 ①交叉验证 ②网格搜索 五.以knn为例的模型调优使用方法 1.对超参数进行构造 2.进行网格搜索 3.结果查看 一.概率知识基础 1.概率 概率就是某件事情发生的可能性. 2.联合概率 包

  • 关于redis状态监控和性能调优详解

    前言 对于任何应用服务和组件,都需要一套完善可靠谱监控方案. 尤其redis这类敏感的纯内存.高并发和低延时的服务,一套完善的监控告警方案,是精细化运营的前提. 本文主要给大家介绍了关于redis状态监控和性能调优的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 1.redis-benchmark redis基准信息,redis服务器性能检测 例如: 检测redis服务器性能,本机6379端口的实例,100个并发连接,100000个请求 redis-benchmark

  • Java并发编程之ThreadLocal详解

    目录 一.什么是ThreadLocal? 二.ThreadLocal的使用场景 三.如何使用ThreadLocal 四.数据库连接时的使用 五.ThreadLocal工作原理 六.小结 七.注意点 一.什么是ThreadLocal? ThreadLocal叫做线程本地变量,ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的.ThreadLocal为变量在每个线程中都创建了一个副本,则每个线程都可以访问自己内部的副本变量. 二.ThreadLocal的使用场景 1.当对象

  • 基于线程、并发的基本概念(详解)

    什么是线程? 提到"线程"总免不了要和"进程"做比较,而我认为在Java并发编程中混淆的不是"线程"和"进程"的区别,而是"任务(Task)".进程是表示资源分配的基本单位.而线程则是进程中执行运算的最小单位,即执行处理机调度的基本单位.关于"线程"和"进程"的区别耳熟能详,说来说去就一句话:通常来讲一个程序有一个进程,而一个进程可以有多个线程. 但是"任务

  • java并发编程之cas详解

    CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替换当前变量的值.这听起来可能有一点复杂但是实际上你理解之后发现很简单,接下来,让我们跟深入的了解一下这项技术. CAS的使用场景 在程序和算法中一个经常出现的模式就是"check and act"模式.先检查后操作模式发生在代码中首先检查一个变量的值,然后再基于这个值做一些操作.下面是一个

  • Java ThreadLocal原理解析以及应用场景分析案例详解

    目录 ThreadLocal的定义 ThreadLocal的应用场景 ThreadLocal的demo TheadLocal的源码解析 ThreadLocal的set方法 ThreadLocal的get方法 ThreadLocalMap的结构 ThreadLocalMap的set方法 ThreadLocalMap的getEntry方法 ThreadLocal的内存泄露 如何避免内存泄露呢 应用实例 实际应用二 总结 ThreadLocal的定义 JDK对ThreadLocal的定义如下: The

  • Java并发编程ThreadLocalRandom类详解

    目录 为什么需要ThreadLocalRandom ThreadRandom原理详解 为什么需要ThreadLocalRandom java.util.Random一直都是使用比较广泛的随机数生成工具类,而且java.lang.Math中的随机数生成也是使用的java.util.Random实例. 我们下面看一下java.util.Random的使用方法: import java.util.Random; public class code_4_threadRandom { public sta

随机推荐