Spring 官宣:換掉 JVM!

使用簡(jiǎn)單mvn spring-boot:build-image或gradle bootBuildImage命令,您可以生成一個(gè)優(yōu)化的容器映像,該映像將包含一個(gè)最小的OS層和一個(gè)小的本機(jī)可執(zhí)行文件,該映像僅隨附JDK,Spring以及您在應(yīng)用程序中使用的依賴(lài)項(xiàng)中的必需位。
請(qǐng)參閱下面的示例,其中包含50MB可執(zhí)行文件的最小容器映像,其中包含Spring Boot,Spring MVC,Jackson,Tomcat,JDK和應(yīng)用程序。

具有Spring Cloud功能的無(wú)服務(wù)器 以更便宜和更可持續(xù)的方式托管Spring微服務(wù) 非常適合VMware Tanzu等Kubernetes平臺(tái) 想要?jiǎng)?chuàng)建最佳的容器映像來(lái)打包您的Spring應(yīng)用程序和服務(wù)
在使用場(chǎng)景上,比如 Piotr Mińkowski 提供了一個(gè)非常棒的指南,介紹了如何在 Knative 上使用 Spring Boot 和 GraalVM 構(gòu)建原生微服務(wù)。
阿里JVM 團(tuán)隊(duì)技術(shù)專(zhuān)家林子熠博士在最新出版的《GraalVM與Java靜態(tài)編譯:原理與應(yīng)用》一書(shū)中,揭秘Oracle GraalVM中Java靜態(tài)編譯技術(shù)的特性、實(shí)現(xiàn)原理、應(yīng)用與調(diào)試技巧,以突破Java“冷啟動(dòng)”桎梏,實(shí)現(xiàn)啟動(dòng)性能“質(zhì)”的飛躍。
作者介紹?:林子熠 博士
Java 誕生至今的 25 年里,憑借其峰值性能高、語(yǔ)言功能強(qiáng)、生態(tài)支持好等特點(diǎn)贏得了語(yǔ)言市場(chǎng)的霸主地位。但 Java 冷啟動(dòng)開(kāi)銷(xiāo)大,而云原生時(shí)代下的應(yīng)用程序短小,啟動(dòng)頻繁,冷啟動(dòng)問(wèn)題的解決機(jī)不容發(fā)。
下圖為典型 Java 應(yīng)用的生命周期:

如圖,Java 應(yīng)用生命周期分為 5 個(gè)階段:VM 初始化階段、APP 初始化階段、APP 初活躍階段、APP 穩(wěn)定執(zhí)行期、結(jié)束階段。
VM 初始化(圖中紅色)和 Class loading(圖中藍(lán)色)的開(kāi)銷(xiāo)為冷啟動(dòng)的根因。阿里巴巴實(shí)現(xiàn)了兩類(lèi)改造:一類(lèi)為改良型技術(shù),調(diào)整優(yōu)化現(xiàn)有 Java 的框架和運(yùn)行模型,另外一類(lèi)為革新型的技術(shù),擺脫原有 Java 框架另起爐灶。
改良型技術(shù)中,阿里巴巴主要實(shí)現(xiàn)了基于傳統(tǒng) CDS(Class Data Sharing)的 EagerAppCDS。傳統(tǒng) CDS 包括 mark、Klass*、fields 三部分,如下圖所示:
下圖為 EagerAppCDS 在阿里巴巴內(nèi)部實(shí)踐的脫敏數(shù)據(jù),如圖所示性能提升效果從 12%~95% 不等。

EagerAppCDS 雖未開(kāi)源但已在阿里云 SAE(Serverless 微服務(wù) PaaS 平臺(tái))上線。線上可公開(kāi)實(shí)測(cè)數(shù)據(jù)中應(yīng)用啟動(dòng)耗時(shí)降低 5%~45%,提升效果與啟動(dòng)時(shí)加載類(lèi)數(shù)量成正比。

除此之外,我們還實(shí)現(xiàn)了以下改進(jìn)型技術(shù):
傳統(tǒng) Java 執(zhí)行模型如下圖所示:Application(應(yīng)用本身)在 libs 的支持下運(yùn)行在 JDK 上在 JVM 中執(zhí)行。
靜態(tài)編譯在 Graal Compiler 編譯器中編譯 Application、libs、JDK,同時(shí)編譯 Substrate VM Runtime,獲得 Native Image。Native Image 包含 code(編譯后的代碼)和 Image heap(存儲(chǔ)數(shù)據(jù))兩部分。Image heap 為運(yùn)行時(shí) heap 的起點(diǎn),直接讀取 Image heap 可以提高運(yùn)行時(shí)的性能。
靜態(tài)編譯必須遵循封閉性原則 (the closed-world assumption),即所有運(yùn)行時(shí)信息均需在編譯時(shí)可見(jiàn)。該原則帶來(lái)兩個(gè)基本問(wèn)題:如何確定封閉的邊界?如何處理 Java 的動(dòng)態(tài)特性?
Java bytecode 編譯為 Native code 時(shí),代碼抽象性降低體積增大,如若編譯所有代碼,Native Image 體積將過(guò)于龐大,因此需確定封閉邊界。SVM 通過(guò)靜態(tài)分析上實(shí)現(xiàn)了從給定入口開(kāi)始確定程序可達(dá)范圍的功能。
該技術(shù)應(yīng)用廣泛,例如 main 函數(shù)調(diào)用 Virtue call 必須先明確其 type,type 和 Virtue call 有時(shí)可唯一綁定,但通常不能唯一綁定。此時(shí)使用靜態(tài)分析技術(shù),可明確 Virtue call type 的可能范圍,實(shí)現(xiàn)封閉。

受靜態(tài)分析本身的特性和能力所限,靜態(tài)分析得到的可達(dá)代碼集合(藍(lán)色)略大于實(shí)際執(zhí)行代碼集合(綠色)。靜態(tài)分析精度越高、冗余越少、image 越小。
靜態(tài)分析無(wú)法分析出 Java 的許多動(dòng)態(tài)特性運(yùn)行時(shí)的行為,如反射、動(dòng)態(tài)代理、JNI、序列化(阿里巴巴貢獻(xiàn),從 21.0 開(kāi)始支持)、動(dòng)態(tài)類(lèi)加載(阿里巴巴貢獻(xiàn),patch 已經(jīng)通過(guò)評(píng)審)等。此時(shí)需提前獲取所需信息,方可封閉此類(lèi)動(dòng)態(tài)特性的觸達(dá)范圍——即需基于配置進(jìn)行動(dòng)態(tài)特性支持。
以反射為例。SVM 提供了 native-image-agent,可記錄 APP 運(yùn)行時(shí)所有的反射。編譯時(shí)只需解析配置文件,即可注冊(cè)反射目標(biāo),擴(kuò)大編譯范圍;同時(shí)獲取反射信息后可放入 ReflectionData 緩存中,將反射調(diào)用替換為直接調(diào)用。運(yùn)行時(shí)如遇反射可查找 ReflectionData,獲取目標(biāo)值,通過(guò) Method.invoke 直接調(diào)用目標(biāo)函數(shù)。
下圖為通過(guò)靜態(tài)編譯和傳統(tǒng) Java 兩種方式,分別用反射調(diào)用空函數(shù) 30 次性能對(duì)比測(cè)試結(jié)果:
靜態(tài)編譯由于所有的類(lèi)均已被編譯因此只有一個(gè)類(lèi)加載器,實(shí)際只執(zhí)行類(lèi)查找功能。
傳統(tǒng) Java 一邊檢查異常一邊運(yùn)行,如遇異常直接處理即可。SVM 考慮到在不同平臺(tái)兼容性,異常處理采用非信號(hào)處理機(jī)制:檢測(cè)無(wú)錯(cuò)方可正常運(yùn)行。該檢測(cè)對(duì)性能影響小。
此外,靜態(tài)編譯的 GC 為 Oracle 開(kāi)源版本中的單線程 stop-and-copy 順序 GC,性能一般。
下圖為 Graal VM 官方的實(shí)驗(yàn)數(shù)據(jù):

如上圖所示,在只執(zhí)行 Hello world 程序時(shí),Native Image 性能次于 C,與 Go 相當(dāng),遠(yuǎn)快于傳統(tǒng) JDK;內(nèi)存使用次于 C,只有 Go 的一半,遠(yuǎn)低于傳統(tǒng) JDK,具有高性能低內(nèi)存占用的優(yōu)點(diǎn)。圖中紅色數(shù)據(jù)為受測(cè)語(yǔ)言數(shù)據(jù)除以 Native Image 數(shù)據(jù)所得比值。
Javac 為 Java 編寫(xiě)的編譯器:可以在 Java 程序中來(lái)調(diào)用 API 編譯,也可用 stand alone 工具編譯。通過(guò) API 調(diào)用,實(shí)際上已完成 VM 啟動(dòng),因此兩者對(duì)比可觀察冷啟動(dòng)帶來(lái)的性能差異。



靜態(tài)編譯的局限性如上表所示:
為實(shí)現(xiàn)封閉性,反射、動(dòng)態(tài)代理、JNI、序列化、動(dòng)態(tài)類(lèi)加載均需要通過(guò)配置支持;
不支持 InvokeDynamic(開(kāi)發(fā)人員使用)、Method Handles(開(kāi)發(fā)人員使用)、Security Manager、多 classloader、Finalizers、過(guò)時(shí) Thread 函數(shù)(如 Thread.stop())等;
Java 程序被靜態(tài)編譯后不再保留 bytecode,因此存在監(jiān)控、調(diào)試方面的問(wèn)題:不支持 JVMTI、JMX、agent,只能使用 GDB 調(diào)試,無(wú)法通過(guò) Eclipse IDE、IntelliJ IDEA 等調(diào)試。
GraalVM 靜態(tài)編譯目前生態(tài)如下:
阿里云:通過(guò)阿里云函數(shù)計(jì)算平臺(tái)進(jìn)行支持部署 serverless Native Image 應(yīng)用,通過(guò) Apache RocketMQ 為 C++ 客戶(hù)端提供使用靜態(tài)編譯的 Java 共享庫(kù);
Spring 社區(qū):發(fā)布了針對(duì)于靜態(tài)編譯 Spring-Native beta 版本,完全支持 Spring 的運(yùn)算;
MICRONAUT:實(shí)現(xiàn)了支持 Native Image 的去反射微服務(wù)框架;
Facebook & Twitter:均在生產(chǎn)環(huán)境下使用 Graal 編譯器代替 C2 編譯器。
總之,在 Serverless 場(chǎng)景下 Java 的冷啟動(dòng)問(wèn)題與應(yīng)用對(duì)快速響應(yīng)、實(shí)時(shí)擴(kuò)展的需求形成突出矛盾。阿里巴巴一方面在現(xiàn)有技術(shù)上不斷改進(jìn),最終形成突破:EagerAppCDS 提升最多 45% 的啟動(dòng)速度;另一方面積極參與開(kāi)源社區(qū)探索創(chuàng)新型的前沿技術(shù),打磨成熟用于實(shí)踐:GraalVM 靜態(tài)編譯技術(shù)最多提升百倍啟動(dòng)速度。但 GraalVM 存在兼容性和改造成本的問(wèn)題,適合新項(xiàng)目。
來(lái)源 | QCon全球軟件開(kāi)發(fā)大會(huì)
嘉賓 | 林子熠? ? ?
整理 | 李慧文
熱門(mén)推薦:
PS:如果覺(jué)得我的分享不錯(cuò),歡迎大家隨手點(diǎn)贊、轉(zhuǎn)發(fā)、在看。

