Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java虚拟机 G1 GC 调优解析

Java虚拟机 G1 GC 调优解析

作者头像
Luga Lee
发布于 2021-12-09 13:05:08
发布于 2021-12-09 13:05:08
1.7K00
代码可运行
举报
文章被收录于专栏:架构驿站架构驿站
运行总次数:0
代码可运行
在上篇文章中,我们解析了 Ja

依据官方 Java 虚拟机的规划,自 Java 9 开始,在实际的生产环境中不再建议使用基于 ConcurrentMarkSweep(CMS)垃圾收集器。根据 JEP-291,已做出此决定以减轻GC 代码库的维护负担并加速新开发。毕竟,Java 9 之后,G1 GC 已成为默认的 GC 算法。(当然,基于不同的环境,Z 垃圾收集器-ZGC 、Shenandoah GC 亦逐渐开始成为主流算法)因此,我们可以根据实际业务场景考虑将我们的应用程序移至该算法。它可能提供比 CMS GC 算法更优的性能特征。由于其参数相对较少,因此调整起来要容易得多。此外,G1 同时也提供了一些选项以从内存中消除重复的字符串,从而可以帮助我们应用减少总体内存占用。

其实,在某些场景下,基于 CMS GC 的性能有的时候往往会比 G1 要高、更优,因此,只有基于实际的性能验证,在官方所约束的环境下,才能决定采用哪种符合业务场景的 GC 策略。由于在更高版本的 Java 版本中已弃用 CMS,故此我们才迫不得已需要将其移植至另一种类型的 GC 策略。

那么,如果我们在生产环境中基于 Java 9 后续的版本(以 11 为例),显性定义 CMS GC 策略,即关键字“ -XX:+ UseConcMarkSweepGC ”,将会出现何种异常呢?我们先来看个简要的场景,具体如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[administrator@JavaLangOutOfMemory cpu ]% java -version
java version "11.0.10" 2021-01-19 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.10+8-LTS-162)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.10+8-LTS-162, mixed mode)

基于上述参数,我们定义了 Java 11 版本环境,接下来,通过运行一个简单的 Java 应用来对比验证 CMS GC 在 Java 9 后续版本环境中的表现情况,具体如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[administrator@JavaLangOutOfMemory cpu ]% cat startup-cms.sh 
#!/bin/bash
java -Xms512M -Xmx512M -XX:+UseConcMarkSweepGC -verbose:gc -XX:+PrintCommandLineFlags -XX:+ExplicitGCInvokesConcurrent -Xlog:gc*=debug:file=/Users/administrator/cpu/admin_gc_log/admin_gc.log:utctime,level,tags:filecount=50,filesize=100M -Duser.timezone=GMT+11 -jar /Users/administrator/cpu/devopsDemo.jar PROBLEM_IO
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[administrator@JavaLangOutOfMemory cpu ]% ./startup-cms.sh 
Java HotSpot(TM) 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.
-XX:+ExplicitGCInvokesConcurrent -XX:InitialHeapSize=536870912 -XX:MaxHeapSize=536870912 -XX:MaxNewSize=178958336 -XX:MaxTenuringThreshold=6 -XX:NewSize=178958336 -XX:OldSize=357912576 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC 
[0.043s][info][gc] Using Concurrent Mark Sweep
Application started!
Starting to write to fileIO-1.txt
Starting to write to fileIO-2.txt
Starting to write to fileIO-3.txt
Starting to write to fileIO-4.txt
Starting to write to fileIO-5.txt
[1.295s][info][gc] GC(0) Pause Young (Allocation Failure) 136M->0M(494M) 7.478ms
Read & write 1000 times to fileIO-5.txt
Read & write 1000 times to fileIO-1.txt
Read & write 1000 times to fileIO-3.txt
Read & write 1000 times to fileIO-4.txt
Read & write 1000 times to fileIO-2.txt
[2.321s][info][gc] GC(1) Pause Young (Allocation Failure) 137M->0M(494M) 3.365ms
Read & write 1000 times to fileIO-4.txt
Read & write 1000 times to fileIO-1.txt
Read & write 1000 times to fileIO-5.txt
Read & write 1000 times to fileIO-3.txt
Read & write 1000 times to fileIO-2.txt
[3.089s][info][gc] GC(2) Pause Young (Allocation Failure) 137M->1M(494M) 3.114ms
Read & write 1000 times to fileIO-5.txt
Read & write 1000 times to fileIO-1.txt
Read & write 1000 times to fileIO-4.txt
^C%

基于上述的执行情况,我们可以看到,如果使用 -XX:+UseConcMarkSweepGC 策略启动应用程序 ,将激活 CMS GC 事件机制,应用程序也能够运行起来,此时将看到以下警告消息:“ Java HotSpot(TM) 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release ” 。关于 CMS GC 的相关内容,可参考之前文章:CMS GC已成过去式。接下来,我们进入本文的主题 Garbage First GC 。

Heap

其实,针对 G1 GC 优化,在之前的文章中有所涉及,大家有兴趣可以查阅,链接为:G1 GC简单优化技巧。本文将会从基础的架构原理角度,基于具体的场景进行阐述,以便大家能够更加深入去理解整个 G1 GC 技术体系。从本质上讲,无论是基于早期的Serial、Parallel、CMS、G1 ,还是即将或已落地的 ZGC、Shenandoah GC,它们之间在某种特定的意义角度还是有相互关联的。

基于对象的生命周期发展,G1 与串行,并行和 CMS GC 没什么不同,都是基于分代垃圾收集器概念,通俗意义上来讲,在大多数对象都死于年轻的前提下,将堆分成若干代。在年轻代中处理(清理)对象比转移到老年代并在那里清理要更加简洁、有效。

基于堆的规划层面,G1 与传统的分代垃圾收集器还是有所差异,在 G1 之前,堆被定义为连续的区域,具体如下图所示:

基于上述参考示意图,我们可以看到堆空间已被定义为不同的代(Generation),即年轻代(由 Eden Space 和 Survivor 等区域组成)和老年代,同时,此空间也是连续的。

相对比传统的而言,G1 GC 在堆空间分配上有很大的不同。G1 将堆划分为多个(通常为2048个)较小的 Region(堆区域),Region 的大小可以通过 G1 HeapRegionSize 参数进行设置,其必须是2的幂,范围允许为 1Mb 到 32 Mb。JVM 会基于堆内存的初始值和最大值的平均数计算分区的尺寸,平均的堆尺寸会分出约 2000 个 Region。分区大小一旦设置,则启动之后不会再变化。因此,堆大小直接改变了区域大小。每个区域都可以分配为伊甸园,幸存者或旧区域。分配给 Eden,Survivor 或 Old 的区域数量是灵活的,由 GC 在运行时确定。具体如下图所示:

注:Humongous regions(巨型对象区域)、Free resgions(未分配区域,也会叫做可用分区)暂未在上述图中进行标注。

GC 阶段

通常,在基于 G1 GC 策略场景中,主要存在3个核心的垃圾收集策略。(严格声明:JDK 10 之前的 G1中的 GC 只有 Young GC 和 Mixed GC。FullGC处理会交给单线程的 Serial Old 垃圾收集器。)

1、Minior GC - 年轻代垃圾收集

也称为“ Young GC ”,一些年轻的 GC 在 Eden 和 Survivor 区 之间进行移动,并最终将其移到 Old 空间。即在我们 New 一个对象(非巨型对象)时,并对其进行空间分配,当所有 Eden Region 使用达到最大阀值并且无法申请足够内存时,会触发一次 Young GC。每次 Young GC 会回收所有 Eden 以及 Survivor 区,并且将存活对象复制到 Old 区以及另一部分的 Survivor 区。到 Old 区的标准就是在 PLAB 中得到的计算结果。因为Young GC 会进行根扫描,所以会进行 Stop the World。

2、Mixed GC - 混合垃圾收集

此阶段涉及一个或多个混合收集,即新一代以及垃圾量最大的许多旧区域(可配置)。在混合收集结束时,G1 确定是否需要另一个混合收集以达到其阈值(可配置)。此后,该循环再次从另一个年轻的 GC 阶段开始。Mixed GC 是 G1 GC 特有的,跟 Full GC 不同的是 Mixed GC 只回收部分老年代的 Region。通常,基于全局角度,Mixed GC 一般会发生在一次 Young GC 后面,为了提高效率,Mixed GC 会复用Young GC 的全局的根扫描结果,毕竟 Stop the World 过程是不可或缺的,从整体层面上来说其缩短了暂停时间。

3、Full GC - 全局垃圾收集

与其他 GC 一样,这是最终的选择。如果应用程序在收集活动信息时内存不足,则可能导致 Stop-the-World 的 Full GC,即年轻代和老年代。具体来讲,G1 在对象复制/转移失败或者没法分配足够内存(比如巨型对象没有足够的连续分区分配)时,会触发Full GC。Full GC 使用的是 Stop the World 的单线程的 Serial Old 模式,所以一旦触发Full GC 则会 STW 应用线程,并且执行效率很慢。其实,从本质上讲,JDK 8 版本的 G1 是不提供 Full GC 事件的处理。G1 GC 和其他分代垃圾收集器的主要目标之一:避免使用昂贵的 Full GC。

GC 调优

与传统的 CMS 相比,G1 GC 可用的 JVM 参数选项明显要少得多( 官方所定义:CMS 72、G1 26、ZGC 8),主要目的是为了减少使用。通常,基于 G1 GC 的 JVM 的基本策略主要取决于2点:堆大小和暂停时间,然后让 JVM 动态修改所需的设置以尝试满足暂停时间目标。如果未达到性能目标,需要考虑基于 GC 监视和日志分析的其他选项。这是一个反复的过程,重要的是要确保有足够的时间和资源分配给此关键任务。

1、Heap 大小

在 JDK 11 之前,若我们在生产环境中使用基于 G1 GC 策略,需要显性定义参数: -XX:+ UseG1GC ,通过设置此关键字,才能使得策略场景生效。毕竟,在 Java 8上,默认 GC 是 CMS GC,而在 Java 11 上,默认 GC 才是 G1 GC。这也意味着,在升级 Java 版本时,除非明确设置了 GC 策略类型,否则其仍然依据厂商所定义的策略执行。除此之外,针对堆的内存分配,建议将 -Xms 和 -Xmx 大小显式设置为相同的值,以避免在应用程序生命周期中堆的动态收缩和增长。具体可以使用以下 JVM 参数选项执行此操作:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-XX:InitialHeapSize(最小Java堆大小)---等同于 -Xms
-XX:MaxHeapSize(最大Java堆大小)---等同于 -Xmx

2、GC Pause 目标

通常,在基于 G1 GC 策略场景中,建议设置暂停时间目标,并让 GC 能够根据实际需要更新堆。除此之外,谨记:除非需要,否则不要设置年轻代(-XX:NewSize -XX:MaxNewSize 或 -Xmn)的大小。否则,当我们显性手工设置了其大小,就意味着放弃了 G1 的自动调优。要设置暂停时间目标,我们需要设置以下 JVM 参数选项(默认值为200),具体如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-XX:MaxGCPauseMillis=500

基于上述参数,然后对其进行性能验证,以检测当前设置是否满足预期的性能目标。毕竟,对这个参数的调优是一个持续的过程,逐步调整到最佳状态。暂停时间只是一个目标,在实际的业务场景中并不能总是得到满足。

3、GC Logging

针对 GC Log 的使用,与早期的 CMS 并无差异,只不过,从 Java 9 开始,统一的JVM 日志记录已经取代了旧的日志记录选项。Java 8 的日志记录选项不能用于 Java11。具体如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-XX:+UseG1GC -XX:InitialHeapSize=2048M -XX:MaxHeapSize=2048M -XX:MaxGCPauseMillis=500 -XX:+DisableExplicitGC -XX:+UseStringDeduplication -XX:+ParallelRefProcEnabled -XX:MaxMetaspaceSize=256m -XX:MaxTenuringThreshold=1 -Xlog:gc=debug:file=/data/logs/admin-web-gc.log:time,uptime,level,tags:filecount=5,filesize=100m

4、其他辅助性参数

针对辅助性优化,主要针对 Mixed GC 的调整,具体涉及以下参数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-XX:InitiatingHeapOccupancyPercent -XX:G1MixedGCLiveThresholdPercent -XX:G1MixedGCCountTarger

针对其他方面的优化,可参考之前的文章,具体链接为:G1 GC简单优化技巧

综上所述,本文所解析的内容主要基于 JDK 8 版本环境,在 Jdk10 及以上版本的 G1 GC 会有更多的优化。Full CG 方面,将提供并发标记的 Full GC 方案:Parallelize Mark-Sweep-Compact。Card Table 的扫描也会得到加速。同时,也对 RSet 进行更新优化,目前的 RSet 会存储在所有的分区里,新版本的 RSet 只需要在 CSet 中,并且是在 Remark 到 Clean 阶段之间并发构建 RSet。这项优化会增加整个并发标记的周期,但是缩减了很多 RSet 的占用空间。另外,对于 PauseTime 会有更精准的处理,在 Mixed GC 的对象拷贝阶段,提供了可放弃拷贝的(Abortable)选项。Mixed GC 会计算下一个 Region 的对象拷贝,如果可能会超过预期的 Pause Time,则会放弃这次拷贝。

至此,关于 Java虚拟机 G1 GC 调优解析相关内容本文到此为止,大家有什么疑问、想法及建议,欢迎留言沟通。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 架构驿站 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JVM性能调优实践—G1垃圾收集器全视角解析
本文将总结一下GC的种类,然后侧重总结下G1(Garbage-First)垃圾收集器的分代,结合open-jdk源码分析下重要算法如SATB,重要存储结构如CSet、RSet、TLAB、PLAB、Card Table等。最后会再梳理下G1 GC的YoungGC,MixedGC收集过程。
王知无-import_bigdata
2021/01/05
4.4K0
JVM性能调优实践——G1 垃圾收集器分析、调优篇
这一篇先简单总结一下GC的种类,然后侧重总结下G1(Garbage-First)垃圾收集器的分代,结合open-jdk源码分析下重要算法如SATB,重要存储结构如CSet、RSet、TLAB、PLAB、Card Table等。最后会再梳理下G1 GC的YoungGC,MixedGC收集过程。
周三不加班
2019/09/04
4.7K0
JVM性能调优实践——G1 垃圾收集器分析、调优篇
可能是最全面的G1学习笔记
最近遇到很多朋友过来咨询G1调优的问题,我自己去年有专门学过一次G1,但是当时只是看了个皮毛,因此自己也有不少问题。总体来讲,对于G1我有几个疑惑,希望能够在这篇文章中得到解决。
阿杜
2018/12/26
9920
G1 垃圾收集器深入剖析(图文超详解)
G1(Garbage First)垃圾收集器,是目前垃圾回收技术最前沿的成果之一。
mikechen的互联网架构
2022/11/02
4.6K0
G1 垃圾收集器深入剖析(图文超详解)
Java Hotspot G1 GC的一些关键技术
G1 GC,全称Garbage-First Garbage Collector,通过-XX:+UseG1GC参数来启用,作为体验版随着JDK 6u14版本面世,在JDK 7u4版本发行时被正式推出,相信熟悉JVM的同学们都不会对它感到陌生。在JDK 9中,G1被提议设置为默认垃圾收集器(JEP 248)。在官网中,是这样描述G1的:
大学里的混子
2019/03/04
6200
【Java虚拟机】JVM垃圾回收器详解
总结:ZGC业界还没大规模使用,更多再实验性观望阶段,还存在变动和争议阶段,如果可能则预计26年~28年成为主流,当下我们开发的采用的垃圾收集器是G1收集器,23~25年会是主流。
互联网小阿祥
2023/05/28
8440
【Java虚拟机】JVM垃圾回收器详解
G1 垃圾回收器
https://blog.51cto.com/u_15278282/3242908
Get
2024/03/21
2000
搞懂系列三: G1垃圾收集器
点击上方蓝色字体,选择“设为星标” 回复”学习资料“获取学习宝典 一.G1 GC术语 1.1 并发   并发的意思是Java应用执行和垃圾收集活动可以同时进行 1.2 并行   并行的意思是垃圾收集运算是多线程执行的,比如CMS垃圾收集器的年轻代就是并行的,并行与串行的区别如下图,左边为串行,右边为并行: 1.3 STW   STW(stop the world)意思是在一个垃圾回收事件中,所有Java应用线程会被暂停。只有暂停,应用才不会产生新的垃圾,有益于垃圾收集器更好的标记垃圾对象。(这就像是
猿天地
2022/06/17
8440
搞懂系列三: G1垃圾收集器
JVM G1(Garbage-First Garbage Collector)收集器全过程剖析
G1垃圾收集器的设计原则是“首先收集尽可能多的垃圾(Garbage First)”,目标是为了尽量缩短处理超大堆(超过4GB)产生的停顿。
斯武丶风晴
2020/05/09
1.4K0
JVM G1(Garbage-First Garbage Collector)收集器全过程剖析
G1垃圾收集器
G1 GC,全称Garbage-First Garbage Collector,通过-XX:+UseG1GC参数来启用,作为体验版随着JDK 6u14版本面世,在JDK 7u4版本发行时被正式推出,相信熟悉JVM的同学们都不会对它感到陌生。在JDK 9中,G1被提议设置为默认垃圾收集器(JEP 248)。在官网中,是这样描述G1的:
黑洞代码
2021/01/14
1K0
G1垃圾收集器
性能优化-垃圾收集器以及内存分配
前面我们讲了垃圾回收的算法,还需要有具体的实现,在jvm中,实现了多种垃圾收集 器,包括:串行垃圾收集器、并行垃圾收集器、CMS(并发)垃圾收集器、G1垃圾收集器,接下来,我们一个个的了解学习。
cwl_java
2020/02/13
4470
G1垃圾收集器概述
开始学习前,抛出两个常见面试问题:1.G1的回收原理是什么?为什么G1比传统的GC回收性能好?2.为什么G1如此完美仍然会有ZGC?简单的回顾下CMS垃圾回收机制,下面介绍了一个极端的场景(而且是经常发生的) 在发生Minor GC时,由于Survivor区已经放不下了,多出的对象只能提升(Promotion)到老年代。但是此时老年代因为空间碎片的缘故,会发生Concurrent mode failure的错误。这个时候,就需要降级为Serial Old垃圾回收器进行收集。这就是比concurrent mode failure 更加严重的promotion failed的问题。一个简单的Minor,竟然能演化成耗时最长的Full GC。最要命的是,这个停顿时间是不可预知的。有没有一种方法,能够首先定义一个停顿时间,然后反向推算收集内容呢?就像是领导在年初制定KPI一样,分配的任务多久多干些,任务少就少干点。类似需要徒步一段很长的路,然后在路中有多个里程碑,到达一个后可以休息一会。G1的思路说起来类似,它不要求每次都把垃圾清理的干干净净,只是努力做它认为对的事情。我们要求G1,在任意1秒的时间内,停顿不得超过10ms,这就是在给它制定KPI。G1会尽量达成这个目标,它能够推算出本次要收集的大体区域,以增量的方式完成收集。这也是使用G1垃圾回收器不得不设置的一个参数:-XX:MaxGCPauseMilis=10
黑洞代码
2021/04/28
1K0
G1垃圾收集器概述
美团 2面:为什么 G1能够替代 CMS回收器?看完这篇你就懂了!
在 《肝了一周,彻底弄懂了 CMS收集器原理,这个轮子造的真值! 》这篇文章中, 详细地分析了 CMS收集器,刚好这两天看了一道美团 2面的题目:G1 为什么能替代 CMS收集器?借此机会,把 G1收集器以及它和 CMS的对比一并彻底讲解。
猿java
2024/03/24
1K1
美团 2面:为什么 G1能够替代 CMS回收器?看完这篇你就懂了!
老大难的GC原理及调优,这下全说清楚了
GC 调优中,GC 导致的应用暂停时间影响系统响应速度,GC 处理线程的 CPU 使用率影响系统吞吐量。
Java识堂
2019/10/12
7810
老大难的GC原理及调优,这下全说清楚了
GC算法和垃圾收集器
垃圾收集器在对堆回收之前,第一件事情就是要确定这些对象哪些还“存活”着,哪些对象已经“死去”(即不可能再被任何途径使用的对象)、
leobhao
2022/06/28
3940
GC算法和垃圾收集器
JVM层GC调优(上)
JVM层的GC调优是生产环境上必不可少的一个环节,因为我们需要确定这个进程可以占用多少内存,以及设定一些参数的阀值。以此来优化项目的性能和提高可用性,而且这也是在面试中经常会被问到的问题。
端碗吹水
2020/09/23
5760
JVM层GC调优(上)
java8 各种GC的总结
标记-清除算法分为标记和清除两个阶段:首先标记出需要回收的对象,在标记完成后统一回收所有被标记的对象。 存在的问题: 一是效率低,标记和清除两个过程效率都不高。二是空间问题,标记清除后会产生大量的不连续的内存碎片。空间碎片太多会导致程序在运行过程中需要分配较大对象时无法找到连续内存而不得不提前触发GC。
冬天里的懒猫
2021/08/13
1K0
java8 各种GC的总结
老大难的GC原理及调优,这下全说清楚了
本文介绍 GC 基础原理和理论,GC 调优方法思路和方法,基于 Hotspot jdk1.8,学习之后你将了解如何对生产系统出现的 GC 问题进行排查解决。
猿天地
2019/10/12
1.6K0
老大难的GC原理及调优,这下全说清楚了
详解 JVM Garbage First(G1) 垃圾收集器
Garbage First(G1)是垃圾收集领域的最新成果,同时也是HotSpot在JVM上力推的垃圾收集器,并赋予取代CMS的使命。如果使用Java 8/9,那么有很大可能希望对G1收集器进行评估。本文详细首先对JVM其他的垃圾收集器进行总结,并与G1进行了简单的对比;然后通过G1的内存模型、G1的活动周期,对G1的工作机制进行了介绍;同时还在介绍过程中,描述了可能需要引起注意的优化点。笔者希望通过本文,让有一定JVM基础的读者能尽快掌握G1的知识点。另,本文较长,建议收藏阅读。
aoho求索
2019/07/08
21K1
基础篇:java GC 总结,建议收藏
堆内存是所有线程共享的,jvm 在并发的环境进行内存分配存在同步竞争,为了加快对象的分配创建,jvm 为每个线程分配了一个私有缓存区域(在Eden空间内),这就是 Thread Local Allocation Buffer。使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量。如果私有 TLAB 使用完,则使用全局的
潜行前行
2022/08/30
5100
基础篇:java GC 总结,建议收藏
推荐阅读
相关推荐
JVM性能调优实践—G1垃圾收集器全视角解析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验