年轻是什么意思(JDK从8岁升级到11岁)
admin
2023-09-10 18:06:39

原文链接:https://www.heapdump.cn/article/2601008

编者按:在HBase业务场景中,我试图将JDK从8升级到11,并使用G1GC作为垃圾收集器,但性能下降了20%。是什么导致了性能下降?如何定位解决?本文介绍了如何利用JFR、火焰图等工具识别问题,并通过逐一版本验证,最终找到导致性能问题的代码。在毕升JDK,每个人都带头解决问题,并最终将其推给上游社区。希望通过本文的介绍,让读者了解如何解决大版本升级中遇到的性能问题;同时也提醒Java开发者正确使用参数(使用前要理解参数的含义)。

HBase官方从2.3开始默认支持JDK11 . x HBase对JDK11的支持意味着h base本身可以通过JDK 11编译,所有相关测试用例通过。因为HBase依赖Hadoop和Zookeeper,而最新的Hadoop和Zookeeper还没有支持JDK11,所以HBase还有一个jira关注JDK11的支持。

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

G1GC从JDK9开始成为默认GC,HBase在新版本中也采用了G1GC。HBase可以在生产环境中使用JDK11吗?笔者尝试使用JDK11运行新的HBase,验证JDK11是否比JDK8有优势。

1环境介绍

的验证方法很简单。构建一个3节点HBase集群并安装HBase。采用的版本是2.3.2。HBase环境建设请参考官网。

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

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

HBA seorg . Apache . Hadoop . h base . performance evaluation-rows=10000-valueSize=8000 random write 5

该命令的意思是创建5个客户端并执行连续写测试。每个客户端一次写入8000字节,总共10000行。

PE使用起来非常简单,是HBase压力测量中非常受欢迎的工具。关于PE的更多用法,请参考相关手册。

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

org . Apache . Hadoop . h base . performance evaluation-write towal=true-noma pred-size=256-table=Test1-in memory compaction=BASIC-pre split=50-compress=snappysequentialwrite 120

JDK分别由JDK8u222和JDK11.0.8测试。切换JDK时,客户端和3-HBase服务器会统一切换。JDK的运行参数是:

-XX: printgc details-XX: use G1 GC-XX: maxgcpausemillis=100-XX:-ResizePLAB

注意:这里禁止使用ResizePLAB,因为业务是根据HBase优化数据设置的。

2测试结果:JDK11性能下降

通过PE测试,运行结束有TPS数据,说明性能。

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

从表中可以很快计算出吞吐量减少,运行时间增加。

结论:与JDK8相比,使用G1GC后JDK11的性能明显下降。

3原因分析

从JDK8到JDK11,G1GC做了很多优化来提升性能。为什么JDK11对用户更不友好?简要总结从JDK8到JDK11的一些主要设计变更,如下表所示:

因为JDK8到JDK11的特性变化太多,为了快速有效的解决这个性能下降问题,我们做了以下尝试。

3.1统一JDK8和JDK11的参数,验证效果

因为JDK11和JDK8变化很大,有些功能完全不同,但是这些变化的功能一般都是由参数控制的。一个有效的尝试就是把JDK8和JDK11关于G1的参数整理出来,设置成相同的值,比如关闭IHOP的自适应,关闭螺纹调整。下面是JDK8和JDK11不同参数的简单对比,如下图所示:

将两个参数设置为与JDK8相同的值,并重新验证测试。结果不变,JDK11的性能依然下降。

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

同时为JDK8和JDK11配置日志收集功能,重新测试获取GC日志。通过对GC日志的分析,我们发现区别主要在G1younggc的objectcopy阶段(基本需要时间)。JDK11的YoungGC需要200ms左右,JDK8的YoungGC需要100 ms左右,两者都设置了100ms的目标暂停时间.

/p>

JDK11中GC日志片段:

JDK8中GC日志片段:

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

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

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

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

总体GC的耗时JDK11更多。

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

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

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

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

3.3.1JDK11总体信息

JDK8中通过JFR收集信息。

3.3.2JDK8总体信息

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

3.3.3JDK11中垃圾回收发生的情况

3.3.4JDK8中垃圾回收发生的情况

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

3.3.5JDK11中VM操作

3.3.6JDK8中VM操作

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

3.4火焰图-发现热点

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

3.4.1JDK11的火焰图

3.4.2JDK11GC部分火焰图

3.4.3JDK8的火焰图

3.4.4JDK8GC部分火焰图

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

在JDK11中,耗时主要在:

1)G1ParEvacuateFollowersClosure::do_void()

2)G1RemSet::scan_rem_set

在JDK8中,耗时主要在:

1)G1ParEvacuateFollowersClosure::do_void()

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

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

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

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

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

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

B66版本为了解决PLABsize获取不对的问题(根据GC线程数量动态调整,但是开启UseDynamicNumberOfGCThreads后该值有问题,默认是关闭)修复了bug。具体见jira:DeterminingthedesiredPLABsizeadjuststothethenumberofthreadsatthewrongplace

B74发现有问题(desired_plab_sz可能会有相除截断问题和没有对齐的问题),重新修改,具体见8079555:REDO-DeterminingthedesiredPLABsizeadjuststothethenumberofthreadsatthewrongplace

B115中发现B74的修改,动态调整PLAB大小后,会导致很多情况PLAB过小(大概就是不走PLAB,走了直接分配),频繁的话会导致性能大幅下降,又做了修复NetPLABsizeisclippedtomaxPLABsizeasawhole,notonaperthreadbasis

重新修改了代码,打印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后JDK8和JDK11性能基本相同,也从侧面说明了该参数的使用情况。

3.7解决方法&修复方法

由于该问题是JDK9引入,在JDK9,JDK10,JDK11,JDK12,JDK13,JDK14,JDK15,JDK16都会存在性能下降的问题。

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

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

当然对于短时间内无法切换JDK的同学,遇到这个问题,该如何解决?难道要等到JDK17?一个临时的方法是显式地设置YoungPLABSize和OldPLABSize的值。YoungPLABSize设置为YoungPLABSize*ParallelGCThreads,其中ParallelGCThreads为GC并行线程数。例如YoungPLABSize原来为1024,ParallelGCThreads为8,在JDK9~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等相关编译技术

相关内容

热门资讯

金花创建房间/微信金花房卡怎么... 1.微信渠道:(荣耀联盟)大厅介绍:咨询房/卡添加微信:88355042 2.微信游戏中心:打开微...
金花房间卡/金花房卡如何购买/... 金花房间卡/金花房卡如何购买/新超圣金花房卡正版如何购买新超圣是一款非常受欢迎的游戏,咨询房/卡添加...
牛牛创建房间/金花房卡批发/神... 微信游戏中心:神牛大厅房卡在哪里买打开微信,添加客服【88355042】,进入游戏中心或相关小程序,...
链接牛牛/牛牛房卡游戏代理/鸿... 鸿运大厅房卡更多详情添加微:33549083、 2、在商城页面中选择房卡选项。 3、根...
科技实测!牛牛房卡怎么获得/乐... 微信游戏中心:乐酷大厅房卡在哪里买打开微信,添加客服【88355042】,进入游戏中心或相关小程序,...