Java并行GC:不必要的完整GC

如何解决Java并行GC:不必要的完整GC

我有一项服务,该服务从源读取数据,对数据进行一些转换,然后将转换后的数据上传到目标。选择GC算法时,我正在寻找一种吞吐量高的算法,这就是为什么我选择并行GC。让我感到困惑的部分是为什么我看到了大量的Full GC。服务的性质使大多数对象随着数据的来去而短暂生存。这是我的GC配置:

-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -verbose:GC -XX:+UseParallelGC -XX:NewSize=21200m -XX:MaxNewSize=21200m -server -Xms31200m -Xmx31200m

基本上,我将总堆大小设置为30GB,将年轻代大小设置为20GB。

这是GC记录的一部分:

2020-11-08T08:31:07.863+0000: 215.876: [Full GC (Ergonomics) [PSYoungGen: 1233347K->0K(18729472K)] [ParOldGen: 9065862K->6633660K(10240000K)] 10299209K->6633660K(28969472K),[Metaspace: 107588K->107588K(1144832K)],1.1350824 secs] [Times: user=21.03 sys=0.00,real=1.14 secs]
2020-11-08T08:31:10.627+0000: 218.640: [GC (GCLocker Initiated GC)
Desired survivor size 2699034624 bytes,new threshold 1 (max 15)
[PSYoungGen: 15874560K->1274938K(19073024K)] 22513996K->7914375K(29313024K),0.1073842 secs] [Times: user=3.10 sys=0.00,real=0.11 secs]
2020-11-08T08:31:12.319+0000: 220.331: [GC (GCLocker Initiated GC)
Desired survivor size 2587885568 bytes,new threshold 1 (max 15)
[PSYoungGen: 17602106K->1307000K(18962944K)] 24253865K->8618788K(29202944K),0.2492961 secs] [Times: user=7.16 sys=0.00,real=0.25 secs]
2020-11-08T08:31:14.197+0000: 222.210: [GC (GCLocker Initiated GC)
Desired survivor size 2480930816 bytes,new threshold 1 (max 15)
[PSYoungGen: 17634168K->1333816K(19286016K)] 24952891K->9297010K(29526016K),0.2524904 secs] [Times: user=7.07 sys=0.00,real=0.25 secs]
2020-11-08T08:31:16.165+0000: 224.178: [GC (GCLocker Initiated GC)
Desired survivor size 2386558976 bytes,new threshold 1 (max 15)
[PSYoungGen: 18092600K->1313137K(19181568K)] 26062932K->9992006K(29421568K),0.2845171 secs] [Times: user=7.85 sys=0.00,real=0.29 secs]
2020-11-08T08:31:18.084+0000: 226.096: [GC (GCLocker Initiated GC)
Desired survivor size 2312110080 bytes,new threshold 1 (max 15)
[PSYoungGen: 18071921K->1242981K(19450880K)] 26751020K->10584632K(29690880K),0.2523254 secs] [Times: user=6.79 sys=0.00,real=0.26 secs]
2020-11-08T08:31:18.336+0000: 226.349: [Full GC (Ergonomics) [PSYoungGen: 1242981K->0K(19450880K)] [ParOldGen: 9341651K->6896991K(10240000K)] 10584632K->6896991K(29690880K),[Metaspace: 107625K->107625K(1144832K)],1.0198299 secs] [Times: user=18.34 sys=0.08,real=1.02 secs]
2020-11-08T08:31:21.049+0000: 229.062: [GC (GCLocker Initiated GC)
Desired survivor size 2221408256 bytes,new threshold 1 (max 15)
[PSYoungGen: 17120256K->1356565K(19378176K)] 24043241K->8279559K(29618176K),0.1089915 secs] [Times: user=3.38 sys=0.00,real=0.11 secs]
2020-11-08T08:31:22.887+0000: 230.899: [GC (GCLocker Initiated GC)
Desired survivor size 2155872256 bytes,new threshold 1 (max 15)
[PSYoungGen: 18476821K->1265473K(19603456K)] 25426058K->8896652K(29843456K),0.2524566 secs] [Times: user=7.14 sys=0.00,real=0.25 secs]
2020-11-08T08:31:24.888+0000: 232.901: [GC (GCLocker Initiated GC)
Desired survivor size 2092433408 bytes,new threshold 1 (max 15)
[PSYoungGen: 18699585K->1388375K(19539456K)] 26345045K->9562491K(29779456K),0.2113546 secs] [Times: user=5.59 sys=0.00,real=0.21 secs]
2020-11-08T08:31:26.819+0000: 234.832: [GC (GCLocker Initiated GC)
Desired survivor size 2030043136 bytes,new threshold 1 (max 15)
[PSYoungGen: 18822487K->1308016K(19726336K)] 27003840K->10002863K(29966336K),0.2078162 secs] [Times: user=6.10 sys=0.00,real=0.21 secs]
2020-11-08T08:31:28.868+0000: 236.881: [GC (GCLocker Initiated GC)
Desired survivor size 2030043136 bytes,new threshold 1 (max 15)
[PSYoungGen: 18990960K->1521040K(19665408K)] 27712283K->10780549K(29905408K),0.2373748 secs] [Times: user=6.60 sys=0.00,real=0.23 secs]
2020-11-08T08:31:29.106+0000: 237.119: [Full GC (Ergonomics) [PSYoungGen: 1521040K->0K(19665408K)] [ParOldGen: 9259509K->7378423K(10240000K)] 10780549K->7378423K(29905408K),[Metaspace: 107653K->107653K(1144832K)],1.0809680 secs] [Times: user=20.55 sys=0.00,real=1.09 secs]

日志中有几件事确实让我感到困惑:

  1. JVM如何确定所需的幸存者大小?为什么只有2.5 GB?为什么在每个软GC中它都有一点变化?为什么老一代的总人数从未改变(10240000K),而年轻一代的总人数却一直在变化?
  2. 为什么* 新阈值始终为1?这不是太过激进以至于无法将事情带入老一代吗?
  3. 在每个软GC之后,年轻一代最有可能拥有约1.3GB的数据,并且有一些数据移至了老一代。这导致旧发电机逐渐变满,而Full GC最终发生了清理旧发电机的麻烦。为什么在每个软GC中将部分数据移至旧一代?幸存者空间似乎足够大。
  4. 如何避免不必要的Full GC,以提高整体吞吐量?

解决方法

我将只解决第4点,因为Sachith的答案是关于前3个的。您选择的GC直到真正需要时才执行旧的(或完整的)GC。 Full gc是最昂贵的,而CPU周期专用于您的工作。使用并发的gc:s会删除部分或全部的完整gc:s,但是您将失去cpu周期。因此,不能保证并发gc实际上会更快。 另外,从您的标志-Xms31200m -Xmx31200m。您将堆的最小和最大大小设置为相同,这意味着VM将不会对堆执行任何人体工程学(调整)操作。 根据您的应用程序性能的重要性以及您是否拥有良好的测试环境,我建议您测试不同的gc:s并查看获得什么样的性能。除了最大堆以外,我还将所有其他设置都使用出厂设置,看看能达到多远。

,

好吧,仅凭简单的解释就无法回答问题;

  1. JVM使用-XX:SurvivorRatio参数来定义幸存者的生成大小。其默认值为-XX:SurvivorRatio=8。这是一个比率,平均生存空间是伊甸园空间的八分之一。对于您的情况,这给您的幸存者空间大小-1/8 * 20GB。根据{{​​3}}文档,这通常对性能并不重要。由于您为年轻一代设置了固定的大尺寸,因此老一代保持不变。对ParallelGC使用-XX:+UseAdaptiveSizePolicy可能有助于调整新旧边界周围的大小。同样,年轻一代较大,GC次要收藏发生的频率较低。似乎这些较小的收藏是您看到生存空间略有缩小和增长的情况。

  2. threshold已由JVM选择用于ParallelGC。按照此this

如果幸存者空间太小,则复制集合会溢出 直接进入终身制。如果幸存者空间也是如此 大时,它们将是无用的空的。在每个垃圾收集中, 虚拟机选择阈值数,即次数 可以在使用权之前复制对象。选择此阈值 让幸存者半饱。

这似乎是一种攻击性行为。但是次要的收集周期相距很远,而且如果允许的阈值也可以更改为15,那么看来。

  1. 如果某些对象在年轻一代中生活了所需数量的垃圾回收周期(如ParallelGC设计),则它们注定要迁移到老一代。您无法保证,年轻一代有多大,长寿的物体将永远留在年轻一代中。年轻一代用于快速分配和解除分配对象,而不适用于寿命长的对象。因此,正如您所观察到的,最终老一辈人被填满并清理干净。

  2. 假设您使用Java 8或更高版本,以提高程序的吞吐量,我想说的是,使用article代替ParallelGC。由于堆非常大,因此G1GC将是理想的选择。 G1GC算法设计为以非常短的暂停时间在非常大的terra字节(TB)堆空间上执行。 G1GC建议在大于6GB(G1GC)的堆上使用。使用G1GC时,如果您的程序可以处理大型-XX:+UseStringDeduplication对象,那么String会很有帮助。该GC将整个堆空间划分为多个小区域,并使用并行线程和并发线程执行收集过程。

还有另外两个实验性GC(Garbage First Garbage Collector TuningZGC),它们分别随Java 11和Java 12发布。这些垃圾回收通过大量垃圾收集大大减少了暂停时间。

更新: ZGC和Shenandoah稳定版本随2020年9月发布的Java 15一起提供。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-