2021年畢昇 JDK 的第一個重要更新來了
2021 年 3 月 31 日,畢昇 JDK update 版本正式釋出,下載方式見文末參考文件[1][2],該版本在同步 OpenJDK 社群 8u282/11.0.10 的基礎上,還包含如下更新,為使用者提供高效能、可用於生產環境的 OpenJDK 發行版。
-
G1 Full GC 優化(畢昇 JDK 11)
-
LazyBox 特性(畢昇 JDK 11)
-
提供鯤鵬硬體加速的 KAEProvider(畢昇 JDK 8)
-
Jmap 併發掃描優化(畢昇 JDK8, 畢昇 JDK11)
-
Bug fixes
G1 Full GC 優化原理及使用
G1 GC(Garbage First Garbage Collector)是一款面向服務端、低延遲的垃圾收集器.當 G1 進行 Full GC 時,會對整個堆進行清理,耗時較長,所以如何調優 Full gc 一直是開發人員奮鬥的目標之一。G1 Full GC 在當前的實現中,主要包括如下四個階段:標記存活物件、計算目標物件的位置、更新引用的位置、移動物件完成壓縮,由於 G1 對存活物件較多的 region 進行回收時(G1 是以 region 為單位來管理 java 堆的),需要移動較多的物件,卻只能回收較少的記憶體,效率低下,因此可以在 G1 的第四階段不對此 region 進行壓縮,減少處理的時間。
優化原理
針對上面這種情況,可以通過如下步驟,對 Full GC 過程進行優化:
-
在標記階段,對每個 region 存活的位元組數進行統計
-
在計算目標物件位置時,把存活比例較高的 region 加入到不進行壓縮的 region 集合
-
更新引用的位置與現有情況保持一致
-
在壓縮時跳過不進行壓縮的 region 集合 為此,我們為使用者提供如下引數來使用該特性:
引數 | 說明 |
---|---|
G1FullGCNoMoving | 開啟選項後,當 G1 進行 Full GC 時,將會啟用此優化功能。此選項預設關閉 |
G1NoMovingRegionLiveBytesLowerThreshold | 指定不進行壓縮的 region 中存活位元組佔比的最小值。即當 region 中的存活位元組數佔比超過設定的值時,將該 region 加入到不進行壓縮的 region 集合。預設值為 98 |
在存活物件佔比較高的 Full GC 中,使用此特性將會減少 Full GC 的停頓時間。
效能測試
Dacapo 是一種可以對程式語言、記憶體管理和計算機體系結構進行測試的 Java 基準測試工具,由以下套件組成:avrora、batik、eclipse、fop、h2、jython、pmd、tomcat、daytrader、xalan、lucene 等,其中 h2 是一種類似於 JDBCbench 的記憶體基準測試,針對銀行應用程式的模型執行許多事務,詳見[3]。這裡對 h2 進行測試。
測試環境:
-
CPU: Kunpeng 920
-
OS: CentOS 7.6
-
JDK: 畢昇 JDK 11.0.10
測試指令碼 script.sh
如下,執行./script.sh h2
,然後即可統計 Full GC 停頓時間。
#!/bin/bash
export java=$JAVA_HOME/bin/java
export java_options="-Xmx1g -Xms1g -XX:ParallelGCThreads=4"
echo $java $java_options
task=$1
for i in `seq 30`
do
echo ">>>>>>>>>>>>>>>>>>>> base $i <<<<<<<<<<<<<<<<<<<<<<"
$java $java_options -Xlog:gc*=info:file=h2-base/gc-$task-base-$i.log -jar dacapo-9.12-bach.jar -t 4 --iterations 5 --size huge --no-pre-iteration-gc $task
done
for i in `seq 30`
do
echo ">>>>>>>>>>>>>>>>>>>> opt $i <<<<<<<<<<<<<<<<<<<<<<"
$java $java_options -XX:+G1FullGCNoMoving -Xlog:gc+phases=trace,gc=info,gc+heap=info,gc+task=info:file=h2-opt/gc-$task-opt-$i.log -jar dacapo-9.12-bach.jar -t 4 --iterations 5 --size huge --no-pre-iteration-gc $task
done
測試結果:其中 Percentile 為箱線圖中的概念,10% 即表示將資料從小到大排序,第 10%個數據提升 11.25%.
測試結果
結論:從圖中可以看到,優化之後可以將 G1 Full GC 的停頓時間降低 3%~11%。
該優化的一部分已合入 OpenJDK 社群[4],剩餘部分正在推進中。
LazyBox 特性介紹
Java 為每種基本型別提供了對應的包裝型別,將基本型別轉換為包裝型別在 Java 中稱為裝箱。由於泛型的存在,在 Java 中會頻繁的進行裝箱拆箱操作,帶來許多額外的開銷,典型例子如下:
<T extends Number>
int add(T a, T b) {
return a.intValue() + b.intValue();
}
LazyBox 特性通過在 Hotspot C2 中推遲裝箱的時機,使 C2 只進行必要的裝箱,減少裝箱操作,提高 C2 生成程式碼的執行效率。
場景分析
對於某些裝箱後的值,在某些路徑下並不會被用到,但還是會忍受裝箱帶來的開銷。比如下面程式碼中的 integer 物件:
int sum = 0;
for (int i = 0; i < 100; i++) {
Integer integer = Integer.valueOf(299);
if (i < 1) { //冷路徑
blackhole.consume(integer); //逃逸
sum+=2;
} else { //熱路徑
sum+=integer.intValue(); //拆箱
}
}
return sum;
當 i>=1 時,程式碼只是想獲取 integer
物件的 int
值,此時沒有必要對 integer
進行裝箱,在此中場景下,使用 LazyBox 可以極大的提高效能。只有等到確實需要裝箱時,再在 C2 中插入裝箱操作。
不過由於在每次需要裝箱時,都會插入裝箱操作,所以在某些場景下可能會導致物件不一致。例如下面的場景:
Data data = new Data();
int value = 299;
Integer a = Integer.valueOf(value);
data.a = a;
data.b = a;
System.out.println(data.a == data.b); //開啟LazyBox後為false
由於將 a
賦值給 data
中的 a
和 b
之前,都在 C2 中插入了裝箱操作,所以導致 data
中的 a
和 b
為不同的物件,所以在使用 LazyBox
時,使用者需要留心這一點。
相關引數如下:
引數 | 說明 |
---|---|
LazyBox | 開啟 LazyBox 特性,該選項為實驗選項,需要開啟-XX:+UnlockExperimentalVMOptions, 同時由於 LazyBox 依賴 AggressiveUnBoxing 優化,所以還需開啟-XX:+AggressiveUnboxing 選項 |
PrintLazyBox | 列印 LazyBox 狀態,用於開發者除錯 |
使用者可通過如下方式使用 LazyBox 特性:
-XX:+UnlockExperimentalVMOptions -XX:+AggressiveUnboxing -XX:+LazyBox
效能測試
測試環境:
-
CPU: Kunpeng 920
-
OS: openEuler 20.03
-
JDK: 畢昇 JDK 11.0.10
本實驗採用工業級測試套件 SPECPower 進行測試,在測試過程中進行了綁核,測試結果表明:結合 畢昇 JDK 以前的優化,相比 OpenJDK 可以提升 SPECPower 10%.
鯤鵬硬體加速的 KAEProvider
KAE(Kunpeng Accelerate Engine)加解密是鯤鵬 920 處理器提供的硬體加速方案,可以顯著降低處理器消耗,提高處理器效率[5].畢昇 JDK 為 Java 使用者提供 KAEProvider,使 Java 開發人員可以直接使用硬體帶來的加速效果,提升加解密效率。
實現
Java 通過 JCA(Java Cryptography Architecture)為開發者提供了良好的介面,開發者只需要實現 SPI(Service Provider Interface)介面,並在 CSP(Cryptographic Service Provider, 下文簡稱 Provider)中進行註冊,即可讓使用者使用自己的加解密實現。舉個例子:當用戶通過MessageDigest.getInstance("SHA-256")
獲取 message digest
物件時,JCA 會依次搜尋註冊的 Provider
,直到找到一種實現為止,大體過程如下[6]:
使用者可通過 java.security
檔案指定各個 Provider 的優先順序,或者在程式碼中通過Security.insertProviderAt(Provider, int)
介面指定.也可以在獲取摘要物件時手動指定從哪個 Provider 中獲取,如MessageDigest.getInstance("SHA-256","KAEProvider")
,這種情況下,JCA 將優先使用使用者指定的 Provider.
畢昇 JDK 當前實現了 MessageDigest(MD5,SHA256,SHA384)
、Cipher(AES-ECB,AES-CBC,AES-CTR,RSA)
、KeyPairGenerator(RSA)
、HMac
等 SPI,並在 KAEProvider
中進行了註冊,其它演算法會在後續版本合入,使用者可通過如下方式來使用 KAEProvider.
-
方式 1: 使用 Security API 新增 KAE Provider ,並設定其優先順序。
Security.insertProviderAt(new KAEProvider(), 1);
-
方式 2:修改 jre/lib/security/java.security 檔案,新增 KAE Provider,並設定其優先順序。
security.provider.1=org.openEuler.security.openssl.KAEProvider
security.provider.2=sun.security.provider.Sun
security.provider.3=sun.security.rsa.SunRsaSign
security.provider.4=sun.security.ec.SunEC
security.provider.5=com.sun.net.ssl.internal.ssl.Provider
security.provider.6=com.sun.crypto.provider.SunJCE
security.provider.7=sun.security.jgss.SunProvider
security.provider.8=com.sun.security.sasl.Provider
security.provider.9=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.10=sun.security.smartcardio.SunPCSC
security.provider.11=sun.security.mscapi.SunMSCAPI
效能測試
JMH(Java Microbenchmark Harness)是 OpenJDK 社群提供的一種對 Java 進行 benchmark 測試的工具,使用方式見[7].畢昇 JDK 已將對應的 JMH 測試用例合入了 openEuler 社群[8],這裡採用對應的用例進行測試。
測試環境:
-
CPU: Kunpeng 920
-
OS: openenuler 20.03
-
KAE: v1.3.10 ,下載連結見[9]
-
JDK: 畢昇 JDK 1.8.0_282
測試結果如下,可以看到,在使用 KAEProvider 後,RSA 加解密效能明顯提升。
測試結果
結論:相比 JDK 預設 Provider 提供的 RSA 加解密,當金鑰長度為 2048 位時,KAEProvider 可以提升 53%~80%,當金鑰長度為 4096 位時,KAEProvider 可以提升 70%~83%。
Jmap 併發掃描介紹
當前 jmap 採用單執行緒對 java 堆進行掃描,掃描速度較慢,並且當對超大堆進行掃描時(大於 200G),容易引起系統卡死。因此可以通過多執行緒來進行掃描,減少卡頓時間。
實現
畢昇 JDK 將社群高版本的 jmap 優化回合到此次釋出中,為 jmap -histo 選項增加指定併發執行緒數的 parallel 引數,使 jmap 可以使用多執行緒對堆進行掃描,有效提高 jmap 的掃描效率,減少掃描時間。具體實現原理可參考[10]。使用者可通過在 jmap -histo 後增加 parallel 引數來使用此特性,如下所示:
-
jmap -histo:live,parallel=3 pid
: 指定併發執行緒數為 3 -
jmap -histo:live,parallel=0 pid
: 使用當前系統可支援的併發執行緒數(-XX:ParallelGCThreads) -
jmap -histo:live,parallel=1 pid
: 使用原有的序列掃描
當前的實現只支援 G1 和 ParallelGC,後續版本將支援 CMS.
Bug fixes
除了上面介紹的一些特性外,畢昇 JDK 還合入了社群高版本中的一些 bug fix 和優化的 patch,為使用者提供穩定、高效能的畢昇 JDK。具體回合 patch 如下:
-
JDK8
-
8231841: AArch64: debug.cpp help() is missing an AArch64 line for pns
-
8254078: DataOutputStream is very slow post-disabling of Biased Locking
-
8168996: C2 crash at postaloc.cpp
-
8140597: Forcing an initial mark causes G1 to abort mixed collections
-
8214418: half-closed SSLEngine status may cause application dead loop
-
8259886: Improve SSL session cache performance and scalability
-
-
JDK11
-
8254078: DataOutputStream is very slow post-disabling of Biased Locking
-
8217918: C2: -XX:+AggressiveUnboxing is broken
-
參考
-
[1] Bishengjdk8下載:http://mirrors.huaweicloud.com/kunpeng/archive/compiler/bisheng_jdk/bisheng-jdk-8u282-linux-aarch64.tar.gz
-
[2] Bishengjdk11下載:http://mirrors.huaweicloud.com/kunpeng/archive/compiler/bisheng_jdk/bisheng-jdk-11.0.10-linux-aarch64.tar.gz
-
[3] dacapo介紹:http://dacapobench.org
-
[4] 8263495: Gather liveness info in the mark phase of G1 full gc:http://github.com/openjdk/jdk/commit/8c8d1b31
-
[5] 鯤鵬加速引擎介紹:http://support.huaweicloud.com/devg-kunpengaccel/kunpengaccel_16_0002.html
-
[6] Java Cryptography Architecture (JCA) Reference Guide:http://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html
-
[7] jmh介紹:http://github.com/openjdk/jmh
-
[8] KAEProvider jmh用例:http://gitee.com/openeuler/bishengjdk-8/tree/master/jdk/test/micro/org/openeuler/bench/security/openssl
-
[9] KAE下載連結:http://github.com/kunpengcompute/KAE/releases
-
[10] 8239290:Add parallel heap iteration for jmap -histo:http://bugs.openjdk.java.net/browse/JDK-8239290
- 玩轉機密計算從 secGear 開始
- openEuler資源利用率提升之道06:虛擬機器混部OpenStack排程
- openGauss Cluster Manager RTO Test
- JVM 鎖 bug 導致 G1 GC 掛起問題分析和解決【畢昇JDK技術剖析 · 第 2 期】
- 手把手帶你玩轉 openEuler | openEuler 的使用
- 681名學生中選!暑期2021開啟火熱“開源之夏”!
- 手把手帶你玩轉 openEuler | 初識 openEuler
- StratoVirt 中的 PCI 裝置熱插拔實現
- 使用 NMT 和 pmap 解決 JVM 資源洩漏問題
- JNI 中錯誤的訊號處理導致 JVM 崩潰問題分析
- Java Flight Recorder - 事件機制詳解
- 畢昇 JDK 8u292、11.0.11 釋出!
- StratoVirt 中的虛擬網絡卡是如何實現的?
- openEuler結合ebpf提升ServiceMesh服務體驗的探索
- 我的openEuler社群參與之旅
- StratoVirt 的中斷處理是如何實現的?
- 看看畢昇 JDK 團隊是如何解決 JVM 中 CMS 的 Crash
- 使用 perf 解決 JDK8 小版本升級後效能下降的問題【畢昇JDK技術剖析 · 第 1 期】
- 2021年畢昇 JDK 的第一個重要更新來了
- 漏洞盒子 × openEuler | 廣邀白帽共築安全的Linux開放應用生態