JDK从8升级到11,使用 G1 GC,HBase性能下降20%。JDK 到底干了什么

window10 安装Java11 并可以和Java8进行自由切换 环境 Java:8和11 前言 公司今年所有的项目陆陆续续升级到了jdk11。 自己家里的电脑,还停留在jdk8,今天我也升下级; 但是考虑到以前的项目可能还得使用jdk8,所以肯定得Java11和Java8共存。 下载jdk11 http://openjdk.java.net/install/index.html 这个页面大部分都是linux系统的; 然后我们点击jdk.java.net ,接着我们选择下载Java SE 11; 下好了后,我们得到这样的文件:openjdk-11+28_windows 阅读详情

编者按:笔者在 HBase 业务场景中尝试将 JDK 从 8 升级到 11,使用 G1 GC 作为垃圾回收器,但是性能下降 20%。到底是什么导致了性能衰退?又该如何定位解决?本文介绍如果通过使用 JFR、火焰图等工具确定问题,最后通过版本逐一验证找到了引起性能问题的代码。在毕昇 JDK 众人率先修复问题最后将修复推送到上游社区中。希望通过本文的介绍让读者了解到如何解决大版本升级中遇到的性能问题;同时也提醒 Java 开发者要正确地使用参数(使用前要理解参数的含义)。
HBase 从 2.3.x 开始正式默认的支持 JDK 11,HBase 对于 JDK 11 的支持指的是 HBase 本身可以通过 JDK 11 编译、同时相关的测试用例全部通过。由于 HBase 依赖 Hadoop 和 Zookeeper,而目前最新的 Hadoop 和 Zookeeper 尚未支持 JDK 11,所以 HBase 中仍然有一个 jira 来关注 JDK 11 支持的问题。

具体参考:
https://issues.apache.org/jira/browse/HBASE-22972。

G1 GC 从 JDK 9 以后就成为默认的 GC,而且 HBase 在新的版本中也采用 G1 GC,对于 HBase 是否可以在生产环境中使用 JDK 11?笔者尝试使用 JDK 11 来运行新的 HBase,验证 JDK 11 是否比 JDK 8 有优势。

1环境介绍

验证的方式非常简单,搭建一个 3 节点的 HBase 集群,安装 HBase,采用的版本为 2.3.2,关于 HBase 环境搭建可以参考官网。

另外为了验证,使用一个额外的客户端机器,通过 HBase 自带的 PerformanceEvaluation 工具(简称 PE)来验证 HBase 读、写性能。PE 支持随机的读、写、扫描,顺序读、写、扫描等。

例如一个简单的随机写命令如下:

hbase org.apache.hadoop.hbase.PerformanceEvaluation --rows=10000 --valueSize=8000 randomWrite 5

该命令的含义是:创建 5 个客户端,并且执行持续地写入测试。每个客户端每次写入 8000 字节,共写入 10000 行。

PE 使用起来非常简单,是 HBase 压测中非常流行的工具,关于 PE 更多的用法可以参考相关手册。

本次测试为了验证读写性能,采用如下配置:

org.apache.hadoop.hbase.PerformanceEvaluation --writeToWAL=true --nomapred --size=256 --table=Test1 --inmemoryCompaction=BASIC --presplit=50 --compress=SNAPPY sequentialWrite 120

JDK 采用 JDK 8u222 和 JDK 11.0.8 分别进行测试,当切换 JDK 时,客户端和 3 一 HBase 服务器统一切换。JDK 的运行参数为:

-XX:+PrintGCDetails -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:-ResizePLAB

注意:这里禁止 ResizePLAB 是业务根据 HBase 优化资料设置。

2测试结果:JDK 11性能下降

通过 PE 进行测试,运行结束有 TPS 数据,表示性能。

在相同的硬件环境、相同的 HBase,仅仅使用不同的 JDK 来运行。同时为了保证结果的准确性,多次运行,取平均值。测试结果如下:

从表中可以快速地计算得到吞吐量下降,运行时间增加。

结论:使用 G1 GC,JDK 11 相对于 JDK 8 来说性能明显下降。

3原因分析

从 JDK 8 到 JDK 11, G1 GC 做了非常多的优化用于提高性能。为什么 JDK 11 对于应用者来说更不友好?简单的总结一下从 JDK 8 到 JDK 11 做得一些比较大的设计变化,如下表所示:

由于从 JDK 8 到 JDK 11 特性变化太多,对于这样的性能下降问题,为了能快速有效的解决,我们做了如下的尝试。

3.1统一 JDK 8 和 JDK 11 的参数,验证效果

由于 JDK 11 和 JDK 8 实现变化很多,部分功能完全不同,但是这些变化的功能一般都有参数控制,一种有效的尝试:梳理 JDK 8 和 JDK 11 关于 G1 的参数,将它们设置为相同的值,比如关闭 IHOP 的自适应,关闭线程调整等。这里简单的给出 JDK 8 和 JDK 11 不同参数的比较,如下图所示:

将两者参数都设置为和 JDK 8 一样的值,重新验证测试,结果不变,JDK 11 性能仍然下降。

3.2GC日志分析,确定JDK 11性能下降点

对于 JDK 8 和 JDK 11 同时配置日志收集功能,重新测试,获得 GC 日志。通过 GC 日志分析,我们发现差异主要在 G1 young gc 的 object copy 阶段(耗时基本在这),JDK 11 的 Young GC 耗时大概 200ms,JDK 8 的 Young GC 耗时大概 100ms,两者设置的目标停顿时间都是 100ms。

JDK 11 中 GC 日志片段:

JDK 8中 GC 日志片段:

我们对整个日志做了统计,有以下发现:

并发标记时机不同,混合回收的时机也不同;

单次 GC 中对象复制的耗时不同,JDK 11 明显更长;

总体 GC 次数 JDK 11 得更多,包括了并发标记的停顿次数;

总体 GC 的耗时 JDK 11 更多。

针对 Young GC 的性能劣化,我们重点关注测试了和 Young GC 相关的参数,例如:调整
UseDynamicNumberOfGCThreads、G1UseAdaptiveIHOP 、GCTimeRatio 均没有效果。

下面我们尝试使用不同的工具来进一步定位到底哪里出了问题。

3.3JFR分析-确认日志分析结果

毕昇 JDK 11和毕昇 JDK 8 都引入了 JFR,JFR 作为 JVM 中问题定位的新贵,我们也在该案例进行了尝试,关于JFR的原理和使用,参考本系列的技术文章:Java Flight Recorder - 事件机制详解。

3.3.1JDK 11总体信息

JDK 8 中通过 JFR 收集信息。

3.3.2JDK 8总体信息

JFR 的结论和我们前面分析的结论一致,JDK 11 中中断比例明显高于 JDK 8。

3.3.3JDK 11中垃圾回收发生的情况

3.3.4JDK 8中垃圾回收发生的情况

从图中可以看到在 JDK 11 中应用消耗内存的速度更快(曲线速率更为陡峭),根据垃圾回收的原理,内存的消耗和分配相关。

3.3.5JDK 11中VM操作

3.3.6JDK 8中VM操作

通过 JFR 整体的分析,得到的结论和我们前面的一致,确定了 Young GC 可能存在问题,但是没有更多的信息。

3.4火焰图-发现热点

为了进一步的追踪 Young GC 里面到底发生了什么导致对象赋值更为耗时,我们使用Async-perf 进行了热点采集。关于火焰图的使用参考本系列的技术文章:使用 perf 解决 JDK8 小版本升级后性能下降的问题

3.4.1JDK 11的火焰图

3.4.2JDK 11 GC部分火焰图

3.4.3JDK 8的火焰图

3.4.4JDK 8 GC部分火焰图

通过分析火焰图,并比较 JDK 8 和 JDK 11 的差异,可以得到:

在 JDK 11 中,耗时主要在:

1)G1ParEvacuateFollowersClosure::do_void()

2)G1RemSet::scan_rem_set

在 JDK 8 中,耗时主要在:

1)G1ParEvacuateFollowersClosure::do_void()

下一步,我们对 JDK 11 里面新出现的 scan_rem_set() 进行更进一步分析,发现该函数仅仅和引用集相关,通过修改 RSet 相关参数(修改 G1ConcRefinementGreenZone ),将 RSet 的处理尽可能地从Young GC的操作中移除。火焰图中参数不再成为热点,但是 JDK 11 仍然性能下降。

比较 JDK 8 和 JDK 11 中
G1ParEvacuateFollowersClosure::do_void() 中的不同,除了数组处理外其他的基本没有变化,我们将 JDK 11 此处的代码修改和 JDK 8 完全一样,但是性能仍然下降。

结论:虽然
G1ParEvacuateFollowersClosure::do_void() 是性能下降的触发点,但是此处并不是问题的根因,应该是其他的原因造成了该函数调用次数增加或者耗时增加。

3.5逐个版本验证-最终确定问题

我们分析了所有可能的情况,仍然无法快速找到问题的根源,只能使用最笨的办法,逐个版本来验证从哪个版本开始性能下降。

在大量的验证中,对于 JDK 9、JDK 10,以及小版本等都重新做了构建(关于 JDK 的构建可以参考官网),我们发现 JDK 9-B74 和 JDK 9-B73 有一个明显的区别。为此我们分析了 JDK 9-B73 输入的代码。发现该代码和 PLAB 的设置相关,为此梳理了所有 PLAB 相关的变动:

B66 版本为了解决 PLAB size 获取不对的问题(根据 GC 线程数量动态调整,但是开启
UseDynamicNumberOfGCThreads 后该值有问题,默认是关闭)修复了 bug。具体见 jira:Determining the desired PLAB size adjusts to the the number of threads at the wrong place

B74 发现有问题(desired_plab_sz 可能会有相除截断问题和没有对齐的问题),重新修改,具体见 8079555: REDO - Determining the desired PLAB size adjusts to the the number of threads at the wrong place

B115 中发现 B74 的修改,动态调整 PLAB 大小后,会导致很多情况 PLAB 过小(大概就是不走 PLAB,走了直接分配),频繁的话会导致性能大幅下降,又做了修复 Net PLAB size is clipped to max PLAB size as a whole, not on a per thread basis

重新修改了代码,打印 PLAB 的大小。对比后发现 desired_plab_sz 大小,在性能正常的版本中该值为 1024 或者 4096(分别是 YoungPLAB 和 OLDPLAB),在性能下降的版本中该值为 258。由此确认 desired_plab_sz 不正确的计算导致了性能下降。

3.6PLAB 为什么会引起性能下降?

PLAB 是 GC 工作线程在并行复制内存时使用的缓存,用于减少多个并行线程在内存分配时的锁竞争。PLAB 的大小直接影响 GC 工作线程的效率。

在 GC 引入动态线程调整的功能时,将原来 PLABSize 的大小作为多个线程的总体 PLAB 的大小,将 PLAB 重新计算,如下面代码片段:

其中 desired_plab_sz 主要来自 YoungPLABSize 和 OldPLABSIze 的设置。所以这样的代码修改改变了 YoungPLABSize、OldPLABSize 参数的语义。

另外,在本例中,通过参数显式地禁止了 ResizePLAB 是触发该问题的必要条件,当打开 ResizePLAB 后,PLAB 会根据 GC 工作线程晋升对象的大小和速率来逐步调整 PLAB 的大小。

注意,众多资料说明:禁止 ResziePLAB 是为了防止 GC 工作线程的同步,这个说法是不正确的,PLAB 的调整耗时非常的小。PLAB 是 JVM 根据 GC 工作线程使用内存的情况,根据数学模型来调整大小,由于模型的误差,可能导致 PLAB 的大小调整不一定有人工调参效果好。如果你没有对 YoungPLABSize、OldPLABSize 进行调优,并不建议禁止 ResizePLAB。在 HBase 测试中,当打开 ResizePLAB 后 JDK 8 和 JDK 11 性能基本相同,也从侧面说明了该参数的使用情况。

3.7解决方法&修复方法

由于该问题是 JDK 9 引入,在 JDK 9, JDK 10, JDK 11, JDK 12, JDK 13, JDK 14, JDK 15, JDK 16 都会存在性能下降的问题。

我们对该问题进行了修正,并提交到社区,具体见Jira:
https://bugs.openjdk.java.net/browse/JDK-8257145;代码见:https://github.com/openjdk/jdk/pull/1474;该问题在JDK 17中被修复。

同时该问题在毕昇 JDK 所有版本中第一时间得到解决。

当然对于短时间内无法切换 JDK 的同学,遇到这个问题,该如何解决?难道要等到 JDK 17?一个临时的方法是显式地设置 YoungPLABSize 和 OldPLABSize 的值。YoungPLABSize 设置为 YoungPLABSize* ParallelGCThreads,其中 ParallelGCThreads 为 GC 并行线程数。例如 YoungPLABSize 原来为 1024,ParallelGCThreads 为 8,在 JDK 9~16,将 YoungPLABSize 设置为 8192 即可。

其中参数 ParallelGCThreads 的计算方法为:没有设置该参数时,当 CPU 个数小于等于 8, ParallelGCThreads 等于 CPU 个数,当 CPU 个数大于 8,ParallelGCThreads 等于 CPU 个数的 5/8)。

3.8小结

本文分享了针对 JDK 升级后性能下降的解决方法。Java 开发人员如果遇到此类问题,可以按照下面的步骤尝试自行解决:

1.对齐不同 JDK 版本的参数,确保参数相同,看是否可以快速重现;

2.分析 GC 日志,确定是否有 GC 引起。如果是,建议将所有的参数重新验证,包括移除原来的参数。本例中一个最大的失误是,在分析过程中没有将原来业务提供的参数 ResizePLAB 移除重新测试,浪费了很多时间。如果执行该步骤后,定位问题可能可以节约很多时间;

3.使用一些工具,比如 JFR、NMT、火焰图等。本例中尝试使用这些工具,虽然无果,但基本上确认了问题点;

3.8小结

本文分享了针对 JDK 升级后性能下降的解决方法。Java 开发人员如果遇到此类问题,可以按照下面的步骤尝试自行解决:
1.对齐不同 JDK 版本的参数,确保参数相同,看是否可以快速重现;
2.分析 GC 日志,确定是否有 GC 引起。如果是,建议将所有的参数重新验证,包括移除原来的参数。本例中一个最大的失误是,在分析过程中没有将原来业务提供的参数 ResizePLAB 移除重新测试,浪费了很多时间。如果执行该步骤后,定位问题可能可以节约很多时间;
3.使用一些工具,比如 JFR、NMT、火焰图等。本例中尝试使用这些工具,虽然无果,但基本上确认了问题点;

4.最后的最后,如果还是没有解决,请联系毕昇 JDK 社区(
https://gitee.com/openeuler/bishengjdk-8)。毕昇 JDK 社区每双周周二举行技术例会,同时有一个技术交流讨论 GCC、LLVM 和 JDK 等相关编译技术

jdk11版本jenkins打包jdk8项目,同时兼容jdk11与jdk8 阅读详情

相关推荐

[OOM] hbase堆外内存溢出底层定位

单台服务器刚发布时 java 进程占用3g,以一天5%左右的速度增长,一定时间过后进程占用接近90%,触发服务器报警,而此时 old 区占用在 50%,未触发 CMS GC,而导致堆外内存溢出。本文记录了完整的排查过程,涉及 JVM、hbase 读写源码、JDK 原理、MAT 等排查工具使用。

列苗 3568

Windows同时安装JDK8和JDK11

注意 :在同一个电脑上不能同时配置多个JDK环境变量,但是我们可以通过修改JDK环境变量中的JAVA_HOME 将值改成相应的JDK安装目录,达到来回切换的目的。之前项目中一直使用JDK8,承Maven接的新项目需要使用JDK11,所以需要在电脑上同时安装8和11两个版本的JDK。重启cmd,输入java -version命令,查看版本,版本为JDK11。打开cmd,输入java -version命令,查看版本,版本为JDK8。输入java -version命令,查看版本,版本仍然为JDK8。

记录我目之所及的世界 5009

时机成熟:升级你的Java JDK版本

新功能和API:新的JDK版本通常会引入新的功能和API,这些功能和API可以提升开发人员的生产力和应用程序的功能。通过示例代码和上述建议,希望能够启发你在合适的时机升级你的Java JDK版本,并从中获得更好的开发体验和应用程序质量。性能改进:新的JDK版本通常会引入性能改进,包括更好的内存管理、垃圾回收算法的优化和运行时性能的提升。通过升级JDK版本,你可以确保你的应用程序在最新的安全标准下运行,减少潜在的安全风险。有时候,一些旧的代码可能不兼容新的JDK版本,需要进行相应的调整和修改。

ScriptCharm的博客 430

关于jdk8和jdk11同时安装后设置环境变量的解决方法

JDK8 和 JDK11 环境变量的设置 和 切换使用。

hanzhen668的博客 4939

秋叶ComfyUI整合包:一键部署AI绘画工作流,解决环境配置难题

在AI绘画和生成式AI领域,Stable Diffusion作为开源的扩散模型,为图像生成提供了强大的技术基础。其核心原理是通过学习数据分布,对随机噪声进行逐步去噪,最终生成高质量的图像。这项技术的价值在于极大地降低了专业图像创作的门槛,并推动了AIGC(人工智能生成内容)在艺术设计、游戏开发、广告营销等场景的广泛应用。然而,其典型工作流实现工具ComfyUI,在实际部署中常因Python环境、PyTorch版本与CUDA驱动之间的复杂依赖关系而受阻,尤其是面对NVIDIA 30系、40系及未来50系等不同

优快云头条 675

【Cadence】stb仿真和ac仿真——以一个简单的全差分反相放大器仿真为例

最近在补一些仿真方法学的问题,以及一些一直以来都有点模糊的概念,简单记录一下。搭建一个以两个CMOS反相器构成的全差分放大器(28nm工艺,PMOS和NMOS尺寸一致)激励的设置方便共模和差模仿真用同一个TB(这里有一个问题:这个放大器需要在开环下仿真还是闭环下?哪些放大器应用在开环,哪些闭环?这种应用场景不同会导致需要仿真得到开环还是闭环增益,或者环路增益?(我知道放大器在设计的时候肯定都是闭环应用的多,但是什么时候没必要闭环应用?光验证ac和stb仿真是否一样的话有必要有回路吗?

weixin_44951108的博客 5万+

Java:使用 G1 GC,HBase 性能下降近20%资料整理

使用 G1 GC,HBase 性能下降近20%资料整理

netyeaxi的专栏 539

Mac用户解决WPS中的俄语显示红线问题(WPS添加外语拼写检查)

我所使用的WPS版本:AppleStore 下载的 MacOs版 3.3.0 首先,对于 在访达-应用程序中,找到WPS,右键-点击“显示包内容” 然后按照下图的索引目录(Contents\Resource\office6\dicts\spellcheck),最后进入一个叫“spellcheck”的文件夹 最后将下载好的字典包(例如上图中的 en_US就代表美式英语;ru_RU就代表俄语) 关于字典包资源下载,我在网上找到一个Google云盘,里面包含了法语,德语,西语,意大利语,葡萄牙.

qq_48159150的博客 7904

hbase 读写调优_hbase优化之旅(二)regionserver的G1 GC优化探索

优化的最终目的是保障用户体验的同时,减少机器,节约成本。为了更好的编写本文,花费20美金。欢迎赞赏支持。g1介绍g1特点g1原理见附录官方文档,本文假设读者对jvm gc和g1原理有基本的了解。g1特点是内存分片(一般1024片),支持动态调整young区大小,old区使用mixed gc方式分成多次小gc,尽量减少单次gc STW(stop the world)暂停时间,让gc对应用延迟的影响在...

weixin_36239866的博客 774

基于YOLO26深度学习框架的人脸识别门禁系统的算法研究实现方法

本文研究了基于YOLO26深度学习框架的人脸识别门禁系统实现方法。系统采用YOLO26目标检测算法,通过CSPDarknet53主干网络、特征金字塔结构和注意力机制实现高效人脸检测。研究内容包括数据预处理、模型训练优化和身份验证等关键技术,使用迁移学习策略和CIoU损失函数提升检测精度。该系统可应用于智能安防场景,具有实时性强、识别准确的特点,为门禁管理提供了智能化解决方案。

一键难忘的博客 766

CloudCompare 二次开发(17)——点云添加均匀分布的随机噪声

使用CloudCompare编程实现点云添加随机噪声。

点云侠的博客 840

hbase 可视化工具_hbase优化之旅(二)regionserver的G1 GC优化探索

优化的最终目的是保障用户体验的同时,减少机器,节约成本。为了更好的编写本文,花费20美金。欢迎赞赏支持。g1介绍g1特点g1原理见附录官方文档,本文假设读者对jvm gc和g1原理有基本的了解。g1特点是内存分片(一般1024片),支持动态调整young区大小,old区使用mixed gc方式分成多次小gc,尽量减少单次gc STW(stop the world)暂停时间,让gc对应用延迟的影响在...

weixin_39616565的博客 450

Flatten 层的深度理解与运用

Flatten 层的深度理解与运用

你要拥有一个你喜欢并且热爱的人生 7038

HBase RegionServer频繁下线?GC停顿与G1调优实战复盘

在分布式系统运维中,JVM垃圾回收(GC)是影响在线服务稳定性的关键因素。当GC停顿过长或过于频繁,轻则增加请求延迟,重则导致节点被协调服务判定为失联。以HBase为例,RegionServer依赖与ZooKeeper的心跳维持存活状态,一旦发生长时间的Full GC或内存碎片化,进程可能来不及发送心跳,从而被集群强制下线。CMS收集器在高分配速率场景下容易因并发模式失败和碎片问题频繁触发停顿。通过分析GC日志、调整JVM参数并切换至G1收集器,可以显著降低停顿时间、控制老年代水位,使RegionServe

weixin_33947521的博客 312

self‑improving‑agent 安装与配置指南(OpenClaw)

长期记忆错误反思用户偏好学习持续进化AI → 反思 → 学习 → 改进。

tiantiantbtb的博客 1342

【深度学习驱动流体力学】Python流体力学Ansys Fluent

PyFluent 是一个将 Python 编程语言与 Ansys Fluent 流体动力学 (CFD) 仿真软件集成的工具。它允许用户通过 Python 脚本来控制和自动化 Fluent 中的仿真任务,实现从预处理、求解到后处理的全流程控制。PyFluent 结合了 Python 的灵活性和 Fluent 的强大功能,提供了一种高效且灵活的 CFD 仿真方法。PyFluent 是一个强大而灵活的工具,使用户能够通过 Python 脚本全面利用 Ansys Fluent 的功能。

源代码杀手的博客 3125

ZGC初体验——OpenJdk 15编译HBase 1.4.8

文章目录ZGC初体验——OpenJdk 15编译HBase 1.4.81. 前言2. G13. ZGC4. 用AdoptOpenJDK15重编译hbase-1.4.84.1 JDK15在滴滴HBase上的应用4.2 准备工作4.3 项目配置4.4 处理sun.misc.Unsafe not found异常4.5 替换javax.xml.ws.http.HTTPException4.6 程序包javax.annotation不存在4.7 程序包com.sun.javadoc不存在4.8升级jetty4.9 提

wolf_333的博客 701

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

上一篇: 为什么微服务一定要有API网关?
下一篇: Nginx超详细常用功能演示,看这一篇就够了
程序员职业指南
博客等级 码龄4年 2138粉丝 960原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值