【jar 很大,你忍一下】GraalVM 21.2 發(fā)布,大量實用性改進
一、前言
GraalVM 21.2已經(jīng)發(fā)布,在這篇文章中,我們將重點介紹這個版本中最明顯、最重要、最令人興奮的更新。
首先,GraalVM 21.2版本可從下列地方下載:
GraalVM:https://www.graalvm.org/downloads GraalVM Enterprise:https://www.oracle.com/downloads/graalvm-downloads.html
GraalVM 由幾個組件組成,在每一個版本中,我們都在努力改進它們。因此,如果你對某個特定組件感興趣,并希望獲得更改的更詳細概述,請參閱文檔[1]。
二、Native Image
讓我們先從Native Image中值得注意的新內(nèi)容開始。早在6月份,我們就為Native Image發(fā)布了新的Gradle和Maven插件,并且提供了最初的JUnit5的支持。它簡化了構(gòu)建你的應(yīng)用程序的Native Image的過程,并且允許用JUnit的方式在Native Image模式下測試你的代碼運行情況。

從最初的版本發(fā)布以來,已經(jīng)有兩個小版本對各種bug進行了修復(fù),改進和清理,所以如果你維護了一個使用 Native Image 的應(yīng)用程序或庫,考慮運行這兩個測試去驗證在 Native Image中的運行情況[2]!
另一個改進是現(xiàn)在 Native Image 會自動從 image 中刪除不必要的 security providers 這些可以被訪問的 security providers 會被靜態(tài)分析程序檢測。這意味著像 --enable-all-security-services 之類的參數(shù)會被棄用,并且在未來會被移除。也可以使用 -H:-EnableSecurityServicesFeature 完全禁用自動檢測并手動注冊provider。你可以從這個文檔[3]中了解到更多細節(jié).
21.2 中一個非常受歡迎的新增功能是實現(xiàn) class 預(yù)定義,以支持運行時的ClassLoader.loadClass調(diào)用。需要在運行時加載的所需 class 必須在構(gòu)建時提供給靜態(tài)分析,以便將它們包含在封閉世界中分析,包含在運行時任意時刻加載類的代碼模式現(xiàn)在可以在 native images 中工作,正如您所期望的那樣。
GraalVM 21.2 的另一個有趣的基礎(chǔ)增強是使用-H:+AllowVMInspection構(gòu)建的 Native Images 現(xiàn)在支持用Java 編寫 JFR 事件[4]。要在運行時記錄 JFR 事件,必須使用命令行選項-XX:+FlightRecorder和-XX:StartFlightRecording來啟用 JFR 支持和 JFR 記錄。實際需要實現(xiàn)的事件并不多,但實現(xiàn)它們或從應(yīng)用程序代碼發(fā)出它們的體系結(jié)構(gòu)是可用的。
你可以嘗試以下示例來查看自定義事件在實踐中的效果:
import jdk.jfr.Event;
import jdk.jfr.Description;
import jdk.jfr.Label;
public class Example {
@Label("Hello World")
@Description("Helps programmer getting started")
static class HelloWorld extends Event {
@Label("Message")
String message;
}
public static void main(String... args) {
HelloWorld event = new HelloWorld();
event.message = "hello, world!";
event.commit();
}
}
將其構(gòu)建到本機映像中,使用-XX:+FlightRecorder -XX:StartFlightRecording=”filename=recording.jfr”運行,你可以在 VisualVM 中看到事件的樣子:

三、編譯器更新
每次更新都會在編譯器中帶來增量的改進和新的優(yōu)化,從而增加一些工作性能。隨著時間的推移,這些收益積累起來,并導(dǎo)致顯著的性能提升。如果你最近沒有更新,現(xiàn)在是利用所有這些改進的好時機。在21.2中,向編譯器添加了許多有趣的優(yōu)化。讓我們從GraalVM Enterprise中可用的組件開始,它們是Oracle Java SE訂閱的一部分。
我們改進了計數(shù)循環(huán)的循環(huán)限制分析,因此編譯器還分析循環(huán)前的控制流,以推斷歸納變量。這可以產(chǎn)生更多的未計數(shù)循環(huán),就像下面的例子一樣,可用于高級優(yōu)化
long i = 0;
if (end < 1) return;
do {
// body
i++;
} while (i != end);
在這里,編譯器可以結(jié)合循環(huán)之前i為0且end ≥ 1的信息來證明end > i并使用該數(shù)據(jù)來優(yōu)化循環(huán)體。
我們還為非計數(shù)循環(huán)添加了一種新穎的條帶挖掘優(yōu)化,它將循環(huán)迭代拆分為多個部分,使它們更易于以后優(yōu)化。默認情況下是禁用的;使用-Dgraal.StripMineNonCountedLoops=true選項啟用它。
我們改進了使用 StringBuilder 模式的代碼的編譯,并通過使基于 JDK11 的 GraalVM 構(gòu)建知道JDK11 中的緊湊字符串來增強對這些模式的支持。
然后就 GraalVM 社區(qū)版而言,編譯器的一個顯著改進是增加了Speculative Guard Movement優(yōu)化,它試圖將循環(huán)不變保護(例如:數(shù)組邊界檢查)從循環(huán)內(nèi)部移動到循環(huán)外部。這可以顯著改善相關(guān)工作性能。
此外,我們改進了long計數(shù)循環(huán)中的安全點消除機制。編譯器現(xiàn)在可以消除具有long歸納變量的循環(huán)中的安全點,它可以靜態(tài)證明歸納變量的范圍是Integer.MAX_VALUE - Integer.MIN_VALUE。思考這個 for 循環(huán)示例:
for (long i = Integer.MIN_VALUE;i< Integer.MAX_VALUE;i++) {
// body
}
編譯器現(xiàn)在可以靜態(tài)證明i只在整數(shù)范圍內(nèi)迭代,即使它是一個long整數(shù)。因此,不需要考慮整數(shù)溢出的情況,循環(huán)的優(yōu)化效果更好。
除此之外,此版本中還有一些可用的優(yōu)化在默認情況下尚未啟用,并且被認為是實驗性的。一項名為Write Sinking的實驗性優(yōu)化嘗試將寫入移出循環(huán)。你可以使用-Dgraal.OptWriteMotion=true啟用它。GraalVM Enteprise 中提供的另一個優(yōu)化(默認情況下尚未啟用)是針對順序代碼的新穎 SIMD(單指令多數(shù)據(jù))矢量化優(yōu)化。你可以使用-Dgraal.VectorizeSIMD=true選項進行實驗。如果你還沒有準備好自己進行實驗,請繼續(xù)關(guān)注 在即將發(fā)布的獨立文章中,我們將詳細探討它給你帶來了什么,以及你的代碼如何從中受益。
四、多語言和Truffle框架
Truffle 受到了一個新的編譯隊列啟發(fā),默認情況下啟用。這種新的啟發(fā)式改進了多語言運行時在工作性能上的預(yù)熱時間。
下面簡要解釋一下它的作用:假設(shè)以下合成的 JavaScript 代碼是你的應(yīng)用程序。循環(huán)運行2個函數(shù),都達到了JIT編譯的閾值,需要編譯:
function lowUsage() {
for (i = 0; i < COMPILATION_THRESHOLD; i++) {
// Do something
}
}
function highUsage() {
for (i = 0; i < 100 * COMPILATION_THRESHOLD; i++) {
// Do something
}
}
while(true) {
lowUsage();
highUsage();
}
在以前的 GraalVM 版本中,優(yōu)先編譯的算法是按先到先得的順序工作的。這將導(dǎo)致首先編譯lowUsage函數(shù),而不是先編譯highUsage的更有利的順序。
現(xiàn)在在 GraalVM 21.2 中,該策略比 hotness 更為復(fù)雜,并考慮了多層設(shè)置,更快地編譯第一層,編譯歷史記錄和取消優(yōu)化對以前確定為重要的代碼進行優(yōu)先級排序,以及代碼 hotness 和同時使用活躍度的加權(quán)組合??偠灾?,這應(yīng)該會為所有 GraalVM 語言帶來更好的預(yù)熱。
有額外的配置選項來調(diào)整啟發(fā)式或完全禁用它的使用。在文檔[5]中了解更多信息。
另一個變化是嵌入多語言上下文時的某些 API 調(diào)用需要新版本的JVMCI,即用于將編譯器插入 Java HotSpot VM 的編譯器接口。所有 JDK 版本的 GraalVM 發(fā)行版都包含新的 JVMCI,但如果你使用具有不同 JDK 的 GraalVM 語言并在模塊路徑上啟用 GraalVM 編譯器,請確保使用以下包含JDK-8264016的 JDK 版本,保證完全的兼容性。

否則使用強制上下文取消 (Context.close(true)) 或中斷 (Context.interrupt(Duration)) 將拋出錯誤。為了獲得最佳體驗,請考慮使用 Graa lVM 發(fā)行版來運行你的應(yīng)用程序或避免使用這些 API。注釋中[6]描述了其他可能的解決方法。
既然我們正在討論的是與 Truffle 相關(guān)的更改,因此 GraalVM 21.2 中有一些令人興奮的更新,用于在 GraalVM 上實現(xiàn)語言和工具的項目。Truffle 庫現(xiàn)在可以在無需事先執(zhí)行的情況下為提前編譯做好準備。有關(guān)更多詳細信息,請參閱 ExportLibrary.useForAOT[7] 和 AOT 教程[8]。
五、JavaScript
JavaScript 實現(xiàn)繼續(xù)添加和更新最新提案的實現(xiàn),如新的 Set 方法[9]、實驗運算符重載支持[10]或 RegExp 匹配索引提案。你可以在發(fā)行說明中找到有關(guān)如何啟用這些功能的詳細信息。
最重要的是,Graal.js 通過在多語言Context跟蹤未處理的 promise rejections 選項來增強開發(fā)體驗。默認情況下,該選項設(shè)置為none,并且不會跟蹤未處理的 promise rejections,但是你可以使用js.unhandled-rejections選項進行配置,并使你的開發(fā)更輕松。
六、Ruby
Ruby 也一如既往地經(jīng)歷了一系列持續(xù)不斷的兼容性和性能改進。一個非常有影響力的附加功能是通過使用每個名稱和每個類的假設(shè)來精確地使 Ruby 方法和常量失效。在加載 Ruby 代碼時,由于 Ruby 的語義,會逐個添加方法,在加載文件時會執(zhí)行大量的代碼。根據(jù)每個類的假設(shè)(這是它在此更改之前在其他 Ruby 實現(xiàn)和 TruffleRuby 中的行為方式),它將使所有調(diào)用該類方法的調(diào)用站點無效。通過精確失效,它只會使下一次調(diào)用實際上需要調(diào)用另一種方法的調(diào)用站點失效。這改善了現(xiàn)實世界代碼的預(yù)熱。
隨著這些更改,我們更新到了 Ruby 2.7.3,除了 resolvstdlib 沒有更新(2.7.3 中的 resolv 有bug[11])。我們還添加了一個新的TruffleRuby::ConcurrentMap數(shù)據(jù)結(jié)構(gòu),用于 ruby 并發(fā)。
有關(guān)許多其他更改和修復(fù),請查看發(fā)行說明[12]。
七、Python
在這個版本中,我們實現(xiàn)了一個比純 Python 版本更快的_pickle,使序列化更快。
改進了對與其他語言的互操作性的支持,使dict類型按照你期望的方式工作,現(xiàn)在使用 Truffle 哈希實現(xiàn)。
與往常一樣,很多工作都集中在性能改進上,特別是在預(yù)熱和共享引擎配置期間!
還有兼容性改進。查看發(fā)行說明或更多詳細信息。
八、LLVM bitcode運行時
在21.2 中有一些與 C++ 相關(guān)的改進。我們修復(fù)了 LLVM 工具鏈在 MacOS 11.3上C工作不正常的問題。我們還通過跨語言互操作性添加了對 C 虛擬調(diào)用的支持。
GraalVM Enterprise 中的管理模式也得到了改善。我們將musl libc更新到 1.2.2 版,并通過在管理模式[13]下添加對pthreads和pthread同步原語的支持,提高了與現(xiàn)有 native 代碼庫的兼容性。
九、FastR
FastR 繼續(xù)致力于兼容性,在 2021-02-01 CRAN 快照中改進其對軟件包的支持:
測試對 3.0.1 的部分支持。 對tibble 3.0.6、vctrs 0.3.6和data.table 1.13.6的主要支持。 對dplyr 1.0.3、ggplot 3.3.3和knitr 1.31的支持正在進行中。
如果你對特定軟件包的支持感興趣,請聯(lián)系我們!
十、WebAssembly
你可以看到我們對 WebAssembly 運行時許多兼容性改進和一些錯誤修復(fù),以通過對盡可能多的使用 WebAssembly 的 NPM 模塊的測試。
十一、Java on Truffle
GraalVM 21.2 for Java on Truffle 中的一項重要內(nèi)容是新的Hotswap 插件 API,它允許重新加載代碼而無需重啟正在運行的應(yīng)用程序。該 API 旨在允許框架開發(fā)人員更好地控制反饋應(yīng)用程序中的更改以響應(yīng) IDE 中的源代碼編輯。它是通過設(shè)置適當?shù)你^子來實現(xiàn)的,其中主要設(shè)計原則是你可以注冊將在指定的 HotSwap 事件上觸發(fā)的各種 HotSwap 偵聽器。例如,當某個服務(wù)提供者的實現(xiàn)發(fā)生變化時,它能夠重新運行靜態(tài)初始值設(shè)定項,運行通用后熱交換回調(diào)或鉤子。
最令人驚奇的是這些API由普通的 Java 調(diào)用組成——比任何基于字節(jié)碼操作的解決方案更容易集成和維護。
有關(guān)更多詳細信息,請查看插件 API 文檔[14]。
對 21.2 進行的另一個非常受歡迎的改進是更好的字節(jié)碼調(diào)度,它可以將 Java 在 Truffle 上使用的解釋器速度提高約15-30%。解釋器速度是影響應(yīng)用程序預(yù)熱速度的一個非常重要的因素。
21.2 還改進了 Java 在 Truffle 對象上實現(xiàn)Map,Map.Entry,List,Iterator或Iterable與其他語言(包 括 Java 主機代 碼)的互操作性。
十二、工具
VisualVM 獲得了許多出色的改進,包括對JDK 17 的支持以及允許從命令行或其他外部工具控制功能。后者可以在安裝了 GraalVM 擴展的情況下實現(xiàn)VisualVM 和 VSCode 之間的無縫集成。你可以添加 IDE 配置集成 VisualVM 來分析你的應(yīng)用程序。

VisualVM 現(xiàn)在還可以保存來自實時 Java 進程的 JFR 記錄,并且在 Profiler 選項卡中有一個新的 Lock Contention 視圖。因此,如果你現(xiàn)在使用 VisualVM 來滿足日常分析需求,它比以往任何時候都更加強大。
十三、文檔
最后但并非最不重要的一點是,我們重構(gòu)了 GraalVM 官網(wǎng)上的文檔?,F(xiàn)在可以在 GitHub 上的主項目[15] 中找到它。
這意味著為 GraalVM 做出貢獻并改善生態(tài)系統(tǒng)中許多其他開發(fā)人員的生活比以往任何時候都要容易。如果你注意到可能使用澄清或不完整描述你最喜歡的功能或調(diào)整選項的文檔,請考慮分享你的專業(yè)知識并通 pull request 幫助項目!如果有疑問,這里有一個關(guān)于如何做到這一點的簡短指南:參與文檔貢獻[16]。
請注意,GraalVM Enterprise 文檔可在docs.oracle.com[17]上找到。
與每個版本一樣,我們要感謝 GraalVM 社區(qū)的所有協(xié)作、反饋和討論,以及分享你如何使用 GraalVM。
請不要猶豫,讓我們知道任何和所有反饋!有多種渠道可以聯(lián)系團隊,選擇最適合你的渠道:Slack、GitHub 或 Twitter,或者向我們發(fā)送郵件。
譯者說:
大家好,我是 如夢技術(shù)春哥(mica 開源作者)感謝深夜還一起參與翻譯和校對的張亞東(JustAuth 開源作者)、吳天狗、老葉等同學。我們已經(jīng)輸出和翻譯了多篇 GraalVM 和 Spring Native 的文章:
文章列表:
翻譯不易,請幫忙分享給更多的同學,謝謝!??!
參考資料
參閱文檔: https://www.graalvm.org/release-notes/21_2/
[2]這兩個測試去驗證在 Native Image中的運行情況: https://medium.com/graalvm/gradle-and-maven-plugins-for-native-image-with-initial-junit-testing-support-dde00a8caf0b
[3]文檔: https://www.graalvm.org/reference-manual/native-image/JCASecurityServices/
[4]Java 編寫 JFR 事件: https://docs.oracle.com/en/java/javase/11/docs/api/jdk.jfr/jdk/jfr/Event.html
[5]文檔: https://github.com/oracle/graal/blob/master/truffle/docs/TraversingCompilationQueue.md
[6]注釋中: https://www.graalvm.org/release-notes/21_2/
[7]ExportLibrary.useForAOT: https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/library/ExportLibrary.html#useForAOT--
[8]AOT 教程: https://github.com/oracle/graal/blob/master/docs/graalvm-as-a-platform/truffle-framework/AOT.md#truffle-aot-tutorial
[9]新的 Set 方法: https://github.com/tc39/proposal-set-methods
[10]實驗運算符重載支持: https://github.com/oracle/graaljs/blob/master/docs/user/OperatorOverloading.md
[11]bug: https://bugs.ruby-lang.org/issues/17748
[12]發(fā)行說明: https://github.com/oracle/truffleruby/releases/tag/vm-21.2.0
[13]管理模式: https://www.graalvm.org/reference-manual/llvm/NativeExecution/#limitations-and-differences-to-managed-execution-on-top-of-graalvm-enterprise
[14]插件 API 文檔: https://www.graalvm.org/reference-manual/java-on-truffle/hotswap-plugin/
[15]GitHub 上的主項目: https://github.com/oracle/graal/tree/master/docs
[16]參與文檔貢獻: https://github.com/oracle/graal/tree/master/docs#readme
[17]docs.oracle.com: https://docs.oracle.com/en/graalvm/index.html
