2021 年 9 月 30 日,毕昇 JDK update Q3 版本正式发布,本次发布将包含 X86_64 版本。此前,毕昇 JDK 只发布 Aarch64 版本,这可能会对运维产生一定的影响,例如需要根据架构构建多个版本以包含不同架构的 JDK,此次毕昇 JDK 同时发布 X86_64 版本以及 Aarch64 版本,将极大的方便用户进行构建,降低维护多个版本的开销。另外,X86_64 版本和 Aarch64 版本共源,所以 X86_64 版本也包含此前毕昇 JDK 团队在 Aarch64 上的功能和大部分优化,在功能和性能方面,两者几乎无差异。欢迎用户安装使用,为产品带来核心竞争力。
此次版本在同步 OpenJDK 社区 8u302/11.0.12 的基础上,还包含如下更新,为用户提供高性能、可用于生产环境的 OpenJDK 发行版。
- PS 优化——Introduce UsePSRelaxedForwardee to enable using relaxed CAS in copy_to_survivor_space(毕昇 JDK8,毕昇 JDK11)
- G1 GC 优化——Parallel Full GC for G1(毕昇 JDK8)
- 提供鲲鹏硬件加速的 KAEProvider(毕昇 JDK11)
- 支持按进程 id 和时间戳生成 jfr 文件(毕昇 JDK8,毕昇 JDK11)
- Bug fixes
1 PS:introduce UsePSRelaxedForwardee to enable using relaxed CAS in copy_to_survivor_space(毕昇 JDK8,毕昇 JDK11)
1.1 背景
在 JDK 中 Parallel Scavenge 是一个高吞吐量 GC,使用非常广泛。在 specjbb 测试中,PSPromotionManager::copy_to_survivor_space 中的 CAS 指令 CPU 占比非常高,主要为 release barrier 导致,分析 PS 逻辑后,CAS 没必要使用 memory barrier,使用 relaxed 可以提高弱内存模型架构上 PS 的性能。
1.2 实现原理
PS 的主要逻辑如下:
- 由上述流程图可以看到,CAS Fail 的线程不会去读 forwardee 内容,因此在弱内存模型的 CPU 架构上,即使 copy obj 和 CAS 乱序,也不会影响 CAS Fail 线程的正确性。
- 关于 work steal 场景,其他线程 steal 到的 obj 能否看到其内容,这个是由 CAS 成功的 push 操作保证的,由于 push 操作底层实现有 release 语义,所以无正确性问题。
使用参数:
UsePSRelaxedForwardee:试验特性开关,默认为 false,表示 PSPromotionManager::copy_to_survivor_space 中 CAS forwardee 使用 release 语义;打开则表示 CAS forwardee 的时候使用 relaxed(无任何 memory barrier),以在弱内存模型 CPU 架构上获取更好性能。
1.3 性能测试
测试环境:
- Architecture: aarch64
- Byte Order: Little Endian
- CPU(s): 128
- On-line CPU(s) list: 0-127
- Thread(s) per core: 1
- Core(s) per socket: 64
- Socket(s): 2
- NUMA node(s): 4
- Vendor ID: 0x48
- Model: 0
- Stepping: 0x1
- BogoMIPS: 200.00
- L1d cache: 64K
- L1i cache: 64K
- L2 cache: 512K
- L3 cache: 65536K
- NUMA node0 CPU(s): 0-31
- NUMA node1 CPU(s): 32-63
- NUMA node2 CPU(s): 64-95
- NUMA node3 CPU(s): 96-127
使用 specjbb2015 进行测试,除 UsePSRelaxedForwardee 开关以外的测试参数如下:
-Xms50g -Xmx50g -XX:+UseParallelGC -XX:ParallelGCThreads=24 -XX:+UseLargePages -XX:LargePageSizeInBytes=2m -XX:+UseBiasedLocking -XX:+AlwaysPreTouch -XX:-UseAdaptiveSizePolicy
测试结果:
测试结果:从上图可以看到,针对 SPECjbb 的 critical,毕昇 JDK8 可以提升 15%,毕昇 JDK11 可以提升 28%
2 Parallel Full GC for G1. (毕昇 JDK8)
2.1 概述
G1 Full GC 是完全的 STW,在此期间应用程序线程完全没有机会运行,长时间停顿会造成用户明显的感知。因此,使用 G1 过程中应尽量避免的 Full GC 的出现,如果出现最好能缩短其时间。当前 JDK 8u 中 G1 Full GC 完全采用串行,包括:
- 各阶段之间,包括标记存活对象、计算目标对象的位置、更新引用的位置、移动对象完成压缩阶段;
- 每个阶段内;
完全的串行导致即使是在多核机器上也无法利用机器的强大性能缩短 Full GC 的(停顿)时间。
由于 G1 Full GC 基本算法的约束,虽然上面提到的四个阶段之间无法并行化,但是各个阶段内却可以通过优化算法做到一定并行化,以达到缩短整体停顿时间的效果。本特性会将计算目标对象的位置、更新引用的位置、移动对象完成压缩三个阶段尽量做到阶段内的并行化。(标记存活对象阶段的并行化后续也会支持)
开启本特性后,可以明显降低 G1 Full GC 的平均停顿时间。本特性属于通用特性,适用于 Aarch64、X86 平台。
2.2 实现原理
2.2.1 并行 Full GC 基本算法
如下列出了并行 Full GC 算法与串行 Full GC 算法的主要差异点:
- 将整个堆分成不同的 heap region set 交给各个 GC 线程分别处理,尽量减少 GC 线程间同步、竞争;
- G1 Full GC 现有实现是将整个堆向一个方向(目标地址)压缩;要做到并行化,并减少并行 GC 线程间的交互、竞争,有效的方式是每个 GC 线程有自己压缩的方向(目标地址)。
- 大对象的特殊处理:在计算目标对象位置并行阶段结束后,才能释放 free 的 humongous region;
2.2.2 计算目标对象位置阶段的并行化
计算目标对象位置阶段主要负责
- 根据标记信息设置对象的 forwardee。
- 释放没有被标记的 humongous regions。
Forwardee 的设置需要预先知道目标地址,该目标地址是通过 Compaction Point 维护着。在遍历 heap region 时每当发现一个新的标记的对象,就将 Compaction Point 里记录的目标地址设置为该对象的 forwardee,然后再将 Compaction Point 里记录的目标地址加上对象的大小,作为下次 forwardee 设置的值。如此往复,直至每一个标记的对象都被 forwarded。
并行地设置对象的 Forwardee 是通过 1)隔离各个 GC 线程的遍历的 heap region,2)隔离各个 GC 线程要为 forwardee 设置的目标地址来达成的。具体实现是,1)通过标记 region 来隔离各个 GC 线程遍历的 heap regions,2)通过为每个 GC 线程维护一个 Compaction Point 来隔离 forwardee 的设置。可以理解为将整个 heap 被分成了 N 份(GC 线程个数为 N),每一份由一个 GC 线程负责,各个线程尽量互不干扰地工作。
除此之外,每个 GC 线程的 Compaction Point 还负责收集属于该 GC 线程的 regions、humongous regions,以便后续(压缩阶段)处理。
Free 的大对象在计算目标对象位置阶段就会被释放。由于大对象的特殊性(可能包括多个 heap region)加之多个 GC 线程在同时工作,需要对其进行一些特殊处理:如,在计算目标对象位置并行阶段结束后,才能释放 free 的 humongous region,以避免多个 GC 线程访问同一个大对象的不同 region 时可能面临的数据不一致问题。
2.2.3 更新引用位置阶段的并行化
更新引用位置阶段主要负责根据对象的 forwardee 信息更新所有引用。
此阶段的并行化比较简单,因为需要的所有信息都只在对象头中(forwardee),并行化和串行化的算法差别很小,不同点只是每个 GC 线程要标记属于自己处理范围的 heap region。
2.2.4 移动对象完成压缩阶段的并行化
移动对象完成压缩阶段主要负责根据对象的 forwardee 信息进行压缩。
每个 GC 线程都有属于自己的 Compaction Point,在计算目标对象位置阶段 Compaction Point 中收集了需要该 GC 线程压缩的 region 的集合。对于单个 GC 线程来说,整个过程与串行差别不大,只是需要从自己的 Compaction Point 中取出 regions,进行压缩。
使用参数:
本特性需要通过 VM option -XX:+G1ParallelFullGC 显示打开,默认为关闭。
注意,本特性会带来如下 JVM 停顿时间上的收益:
- 降低单次 G1 Full GC 的停顿时间;
- 降低总的 G1 Full GC 的停顿时间;
但是,有可能会增加 G1 Full GC 的频率。所以,当降低 JVM 的停顿时间是应用程序的性能调优目标之一时,且 G1 Full GC 是停顿原因之一时,适用于打开 G1ParallelFullGC VM Option,降低单次平均、总的停顿时间。
2.3 性能测试
测试套:Dacapo
测试参数:
- JVM:-Xmx1g -Xms1g -XX:ParallelGCThreads=$N
- Dacapo:-t 4 --iterations 5 --size huge --no-pre-iteration-gc h2
下面分别给出了并行 GC 线程数量分别为 4、16 时 Full GC 停顿时间的数据
- N == 4
- N == 16
测试结果:受益(STW 时间减少)基本在 16%~40%。
3 提供鲲鹏硬件加速的 KAEProvider(毕昇 JDK11)
该特性已在早期的毕昇 JDK 8u282 中支持,详见2021 年毕昇 JDK 的第一个重要更新来了,并在 8u292 版本中对其功能进行完善,详见毕昇 JDK 8u292、11.0.11 发布!, 此次将在毕昇 JDK11 中对该特性进行支持。
3.1 实现原理和性能测试
实现原理和性能测试请参考鲲鹏硬件加解密特性详解. 但由于 JDK11 引入了模块系统,因此用户使用时需要将 KAEProvider 所在的模块(jdk.crypto.kaeprovider)进行导出,如下为毕昇 JDK11 中 KAEProvider 相关的文件:
具体导出命令可参考如下格式:
- 1.
4 支持按进程 id 和时间戳生成 jfr 文件(毕昇 JDK8,毕昇 JDK11)
4.1 说明
该特性用来扩展 JFR 文件名,支持在文件名中加入进程号或时间戳或两者都加,当用户在环境上生成多个 jfr 文件时,该特性可以帮助用户根据需要快速定位到所需的文件。
4.2 功能测试
未合入此特性:
java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:StartFlightRecording=duration=10s,filename=myrecording%t.jfr While
合入此特性:
java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:StartFlightRecording=duration=10s,filename=myrecording%t.jfr While
5 Bug fixes
除了上面介绍的一些特性外,毕昇 JDK 还合入了社区高版本中的一些 bug fix 和优化的 patch,为用户提供稳定、高性能的毕昇 JDK。具体回合 patch 如下:
- JDK8
- 8197387:jcmd started by "root" must be allowed to access all VM processes 允许通过 root 启动的 jcmd 访问环境上任意的 JVM 进程,默认情况下,进程只能被启动该进程的用户通过 jcmd 访问。
- 8069191:moving predicate out of loops may cause array accesses to bypass null check 修复 c2 在 aarch64 上可能会 crash 的 bug
- 8167014: jdeps: Missing message: warn.skipped.entry 该修复可以解决通过 jdeps 解析特定的 jar 包出现的 Missing message: warn.skipped.entry 错误
- 8268453: sun/security/pkcs12/EmptyPassword.java fails with Sequence tag error 该修复可以解决当对密码为空的 KeyStore 进行解析时,可能会出现的 java.io.IOException: Sequence tag error 问题
- 8202142:jfr/event/io/TestInstrumentation is unstable JDK 自带用例修复
- 8143251:HeapRetentionTest.java Test is failing on jdk9/dev 该修复可以解决 G1 GC 在特定场景下导致进程假死的问题
- 8183543:Aarch64: C2 compilation often fails with "failed spill-split-recycle sanity check" 修复 C2 编译器在某些场景下编译方法时报 failed spill-split-recycle sanity check
错误,导致方法被解释执行,进而造成应用程序性能下降的问题
- JDK11
- 8268427: Improve AlgorithmConstraints:checkAlgorithm performance 该 patch 可以提升 TLS 的握手性能
- 8257145: Performance regressionwith -XX:-ResizePLABafter JDK-8079555 该 patch 可以修复使用 G1 GC 后,HBase 性能下降的问题,详细原理可参考毕昇 JDK 以前的文章JDK 从 8 升级到 11,使用 G1 GC,HBase 性能下降近 20%。JDK 到底干了什么?
- 8247691:[aarch64] Incorrect handling of VM exceptions in C1 deopt stub/traps 该修复可以解决 C1 编译器生成指令过程中使用错误的寄存器,进而导致进程 Crash 的问题
6 参考
[1] 毕昇 JDK8 aarch64 下载:https://mirrors.huaweicloud.com/kunpeng/archive/compiler/bisheng_jdk/bisheng-jdk-8u302-linux-aarch64.tar.gz
[2] 毕昇 JDK8 x86_64 下载:https://mirrors.huaweicloud.com/kunpeng/archive/compiler/bisheng_jdk/bisheng-jdk-8u302-linux-x64.tar.gz
[3] 毕昇 JDK11 aarch64 下载:https://mirrors.huaweicloud.com/kunpeng/archive/compiler/bisheng_jdk/bisheng-jdk-11.0.12-linux-aarch64.tar.gz
[4] 毕昇 JDK11 x86_64 下载:https://mirrors.huaweicloud.com/kunpeng/archive/compiler/bisheng_jdk/bisheng-jdk-11.0.12-linux-x64.tar.gz
微信公众号 - openEuler(openEulercommunity)。
如有侵权,请联系 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。