自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

基于靜態(tài)編譯構建微服務應用

開發(fā)
上述過程既給 Java 程序帶來了其他編程語言不具備的優(yōu)勢,比如跨平臺,易上手等。但同時也給 Java 程序帶來了一些性能問題,比如啟動速度慢和運行時內(nèi)存占用高等。

Java 的局限性

傳統(tǒng)的一個 Java 應用從代碼編寫到啟動運行大致可以分為如下步驟:

  1. 首先,編寫 .java 源代碼程序。
  2. 然后,借助 javac 工具將 .java 文件翻譯為 .class 的字節(jié)碼,字節(jié)碼是 Java 中非常重要的內(nèi)容之一,正是因為它的出現(xiàn),Java 才實現(xiàn)對底層環(huán)境的屏蔽,達到 Write once, run anywhere 的效果!
  3. 基于步驟 2 的 .class 文件會被打包成 jar 包或者 war 包進行部署執(zhí)行,部署過程中通過 Java 虛擬機加載應用程序然后解釋字節(jié)碼運行業(yè)務邏輯。

整個過程如下圖所示:圖片

圖 1:Java 程序運行過程

上述過程既給 Java 程序帶來了其他編程語言不具備的優(yōu)勢,比如跨平臺,易上手等。但同時也給 Java 程序帶來了一些性能問題,比如啟動速度慢和運行時內(nèi)存占用高等。

冷啟動問題

圖 1 中 Java 程序啟動運行詳細過程如下圖 2 所示:

圖片

圖 2:Java 程序的啟動過程分析[1]

一個 Java 應用啟動過程首先需要加載該應用程序?qū)?JVM 虛擬機軟件程序到內(nèi)存中,如上圖紅色部分描述所示。然后 JVM 虛擬機再加載對應的應用程序到內(nèi)存中,該過程對應上圖中的淺藍色類加載(Class Load,CL)部分。在類加載過程中,應用程序就會開始被解釋執(zhí)行,對應上圖中淺綠色部分。解釋執(zhí)行過程 JVM 對垃圾對象進行回收,對應上圖中的黃色部分。隨著程序的運行的深入,JVM 會采用及時編譯(Just In Time,JIT)技術對執(zhí)行頻率較高的代碼進行編譯優(yōu)化,以便提升應用程序運行速度。JIT 過程對應上圖中的白色部分。經(jīng)過 JIT 編譯優(yōu)化后的代碼對應圖中深綠色部分。

經(jīng)過上述分析,不難看出,一個 Java 程序從啟動到達到被JIT動態(tài)編譯優(yōu)化會經(jīng)過 VM init,App init 和 App active 幾個階段,相比于其他一些編譯型語言,其冷啟動問題比較嚴重。

運行時內(nèi)存占用高問題

除了冷啟動問題,從上述分析中不難看出,一個 Java 程序運行過程中,什么都不做首先就需要加載一個 JVM 虛擬機,該操作一般占用一定內(nèi)存。另外,由于 Java 程序是先解釋執(zhí)行字節(jié)碼,然后再做 JIT 編譯優(yōu)化。

由于相比于一些編譯型語言其將編譯優(yōu)化的動作后置到運行時,因此非常容易出現(xiàn)實際加載的代碼比實際需要運行的代碼多很多的情況,造成了一些無效內(nèi)存占用情況。綜上所述就是為什么很多人常詬病 Java 程序運行內(nèi)存占用高的幾點主要原因。

更輕量化的 Java 程序

靜態(tài)編譯技術

既然,先解釋執(zhí)行再動態(tài)編譯的 Java 傳統(tǒng)程序運行方式存在上述諸多問題,那有沒有一些方式可以讓 Java 程序也跟其他程序語言,比如 C/C++ 一樣,先編譯后執(zhí)行解決上述問題呢?

答案是肯定的,提前編譯(Ahead-of-Time Compilation,AOT Compilation)或者叫靜態(tài)編譯在 Java 領域很早就被提了出來。其核心思想就是將 Java 程序的編譯階段提前到程序啟動前,然后在編譯階段進行代碼編譯優(yōu)化,讓程序啟動既巔峰,消除冷啟動,降低運行時內(nèi)存開銷。

Java 領域靜態(tài)編譯的實現(xiàn)技術有很多,其中最具代表性的還屬 Oracle 推出的 GraalVM 開源高性能多語言運行時平臺[2]。看到這里有的讀者可能會問:“高性能多語言運行時平臺是什么?它跟靜態(tài)編譯本身有什么關系?”。

圖片

圖 3:GraalVM 多語言運行時平臺

如上圖 3 所示,GraalVM 中通過提供 Truffle 解釋器實現(xiàn)框架,讓開發(fā)人員可以使用 Truffle 提供的 API 快速實現(xiàn)特定語言的解釋器從而實現(xiàn)對上圖中各種編程語言所寫的程序都能進行編譯運行的效果,從而成為一個多語言運行時平臺。GraalVM 實現(xiàn)靜態(tài)編譯能力的編譯器就是 GraalVM JIT Compiler。靜態(tài)編譯框架和運行時由 Substrate VM 子項目實現(xiàn),兼容 OpenJDK 運行時實現(xiàn),提供了原生鏡像程序運行時的異常處理、同步調(diào)度、線程管理、內(nèi)存管理等功能。

因此,GraalVM 不僅可以作為一個多語言運行時平臺,而且由于其中提供的 GraalVM JIT Compiler 靜態(tài)編譯器,其可用來對 Java 程序進行靜態(tài)編譯。

說完靜態(tài)編譯和 GraalVM 之間的關系,有的讀者可能會好奇,基于 GraalVM 的靜態(tài)編譯與常規(guī)的 JVM 解釋執(zhí)行方式有哪些區(qū)別?基于靜態(tài)編譯的 Java 程序相比于目前應用廣泛的 JVM 運行時編譯 Java 程序整個從代碼編寫到編譯執(zhí)行的區(qū)別如下圖 4 所示:

圖片

圖 4:靜態(tài)編譯與傳統(tǒng) JVM 運行過程對比

相比于 JVM 運行時方式,靜態(tài)編譯在運行之前會先對程序解析編譯,然后生成一個跟運行時環(huán)境強相關的 native image 可執(zhí)行文件,最后直接執(zhí)行該文件即可啟動程序進行執(zhí)行。

說到這里可能有的讀者又好奇,上圖 4 中的靜態(tài)編譯過程到底會對 Java 程序做哪些解析操作?靜態(tài)編譯后的可執(zhí)行程序垃圾回收問題怎么解決?如下圖 5 所示,其描述了 GraalVM 靜態(tài)編譯技術實現(xiàn)中編譯過程的輸入與輸出內(nèi)容。

圖片

圖 5:靜態(tài)編譯輸入輸出

圖 5 中左側前三個輸入內(nèi)容 Applicaton,Libraries 和 JDK 是一個 Java 程序編譯運行必備的三部分,不必多說。而 Substrate VM 就是 GraalVM 中實現(xiàn)靜態(tài)編譯的核心部分,在整個靜態(tài)編譯過程中扮演了重要作用。

其中在靜態(tài)分析過程中,如上圖 5 中間部分中所繪制,Substrate VM 通過上下文不敏感的指向分析(Points-to Analysis)來對應用程序做靜態(tài)分析,其可以在不需要運行程序的情況下,基于源程序分析給出所有可能的可達函數(shù)列表然后作為后續(xù)編譯階段的輸入對程序進行靜態(tài)編譯。該過程由于靜態(tài)分析的局限性,無法覆蓋 Java 中的反射、動態(tài)代理、JNI 調(diào)用等動態(tài)特性。這也造成了很多的 Java 框架由于在實現(xiàn)過程中使用了大量的上述特性,因此,都難以直接基于 Substrate VM 完成對自身所有代碼的靜態(tài)分析,需要通過額外的外部配置[3]來解決靜態(tài)分析本身的不足。

例如像 Spring 社區(qū)因此開發(fā)了 AOT Engine[4]如下圖 6 所示來幫助解決 Spring 項目對其中的反射,動態(tài)代理等內(nèi)容進行靜態(tài)分析處理并將其轉換為 Substrate VM 能在編譯階段可識別的內(nèi)容,確保對 Spring 應用可基于 Substrate VM 順利完成靜態(tài)編譯。

圖片

圖 6:Spring AOT Engine

在靜態(tài)分析完成后,基于靜態(tài)分析結果的可達函數(shù)列表,調(diào)用上文介紹的 GraalVM 中的 GraalVM JIT Compiler 編譯器將應用程序編譯為與目標平臺強相關的本地代碼以完成編譯過程。

編譯完成后,就會進入到上圖 5 右側 Native 可執(zhí)行文件生成階段。在該過程中,Substrate VM 會將靜態(tài)編譯階段確定和初始化的內(nèi)容以及跟 Substrate VM 運行時以及 JDK 庫中的數(shù)據(jù)一起保存到最終可執(zhí)行文件的 Image Heap 中。其中 Substrate VM 運行時就為最終可執(zhí)行文件提供了運行過程中所需的垃圾回收、異常處理等能力。對于垃圾回收這塊,在一開始的 GraalVM 社區(qū)版中僅提供了 Serial GC。企業(yè)版中提供了能力更強的 G1 GC。不過在最新的社區(qū)版中 GraalVM 團隊也引入了 G1 GC[5]以便為廣大開發(fā)者提供更強大的靜態(tài)編譯使用能力。

適配 GraalVM 靜態(tài)編譯

上節(jié),簡單介紹了靜態(tài)編譯技術以及其本身的局限性以后,很多外部社區(qū)開發(fā)者這時可能會疑問,一個 Java 開源項目如何快速進行靜態(tài)編譯適配?對于這個問題,其實最核心要解決的本質(zhì)問題就是將開源框架中的 GraalVM 無法識別和處理的動態(tài)內(nèi)容轉換為其可識別的內(nèi)容即可。因此該問題由于不同框架情況不一樣,因此解決方式也會有一些差異。例如在 Spring 中,針對其自身框架開發(fā)的 AOT Engine 可以解決其框架提供的通過 @Configuration 注解注冊類初始化過程無法在靜態(tài)編譯階段被識別、提前在靜態(tài)編譯期生成原本在運行階段才能生成的動態(tài)代理類解決直接靜態(tài)編譯代理類無法被有效生成等問題[6]從而實現(xiàn) Spring 應用的靜態(tài)編譯適配。

對于很多基于 Spring 實現(xiàn)的開源框架,如果本身無法被 GraalVM 識別的動態(tài)特性都是由于 Spring 標準的那一套用法所導致,由于自身屬于 Spring 體系,靜態(tài)編譯過程就肯定少不了 Spring AOT Engine 的參與,因此,框架自身就不需要再提供任何適配就可以具備靜態(tài)編譯能力。

對于非 Spring 體系項目或者自身使用了一些 JDK 中原生的反射或者其他 Java 動態(tài)特性,針對自身代碼中的 Java 動態(tài)用法需要在項目中提供對應的靜態(tài)配置文件才能在靜態(tài)編譯過程中讓編譯器識別其中的動態(tài)特性,對其進行編譯構建才能實現(xiàn)項目的順利編譯與執(zhí)行。針對這種情況,GraalVM 提供了一個名叫 native-image-agent 的 Tracing Agent 來幫助大家更方便地收集元數(shù)據(jù)并準備配置文件。該 Agent 會在常規(guī) Java VM 上的應用程序運行過程中自動收集其中的動態(tài)特性使用情況并將其轉換為 GraalVM 可以識別的配置文件。最后,將通過 Agent 生成的框架自身的動態(tài)配置文件存放在項目的:META-INF/native-image/<group.id>/<artifact.id> 目錄下,就可以在靜態(tài)編譯過程中根據(jù)這些配置內(nèi)容,識別項目包中的動態(tài)特性。

Spring Cloud Alibaba 2022.0.0.0 版本所包含的所有中間件客戶端目前已完成了構建 GraalVM 原生應用的適配。由于項目自身的特定,項目整體實現(xiàn)中有大量的 Spring 語法導致的無法被 GraalVM 識別的動態(tài)特性用法,這塊內(nèi)容直接交由 Spring AOT Engine 來進行解決,社區(qū)未做額外適配工作。

除了 Spring 體系語法,項目本身還是有一些其他 Java 動態(tài)用法的,這塊社區(qū)通過 native-image-agent 來進行解析與動態(tài)配置生成。

基于靜態(tài)編譯構建微服務

Spring Cloud Alibaba 2022.0.0.0 版本所包含的所有中間件客戶端已完成了構建 GraalVM 原生應用的適配。為用戶提供了開箱即用的靜態(tài)編譯能力。相關功能體驗過程如下:

環(huán)境準備

首先需要在首先在機器上安裝 GraalVM 發(fā)行版。您可以在 Liberica Native Image Kit 頁面上手動下載它,也可以使用像 SDKMAN! 這樣的下載管理器。本文演示環(huán)境為 MacOS,如果是 Windows 可參考相應文檔[7]進行操作。執(zhí)行以下命令安裝 GraalVM 環(huán)境:

$ sdk install java 22.3.r17-nik
$ sdk use java 22.3.r17-nik

通過檢查 java -version 的輸出來驗證是否配置了正確的版本:

$ java -version
openjdk version "17.0.5" 2022-10-18 LTS
OpenJDK Runtime Environment GraalVM 22.3.0 (build 17.0.5+8-LTS)
OpenJDK 64-Bit Server VM GraalVM 22.3.0 (build 17.0.5+8-LTS, mixed mode)

應用構建

要使用 GraalVM 靜態(tài)編譯能力構建微服務,首先確保您項目的 Spring Boot 版本為 3.0.0 或以上,Spring Cloud 版本為 2022.0.0 或以上。然后在項目中引入 Spring Cloud Alibaba 2022.0.0.0 版本的所需模塊依賴即可。

通過以下命令生成應用中反射、序列化和動態(tài)代理所需的 Hints 配置文件,前提是應用中引入了 spring-boot-starter-parent 父模塊:

$ mvn -Pnative spring-boot:run

之后應用會啟動,進行預執(zhí)行,需要盡可能完整的測試一遍應用的所有功能,保證應用的大部分代碼都被測試用例覆蓋,該過程會基于 GraalVM 的 native-image-agent 收集程序中的動態(tài)特性,這樣可以確保完整生成應用運行過程中的所有必須的動態(tài)屬性。運行完所有測試用例后,我們發(fā)現(xiàn) resource/META-INF/native-image 目錄下會生成以下一些 hints 文件:

  • resource-config.json:應用中資源 hint 文件
  • reflect-config.json:應用中反射定義 hint 文件
  • serialization-config.json:應用中序列化內(nèi)容 hint 文件
  • proxy-config.json:應用中 Java 代理相關內(nèi)容 hint 文件
  • jni-config.json:應用中 Java Native Interface(JNI)內(nèi)容 hint 文件

注意事項:Spring Cloud Alibaba 2022.0.0.0 正式版本所有核心模塊都已經(jīng)默認將自身組件相關動態(tài)特性所需的配置內(nèi)容都包含在了依賴中,因此上述預執(zhí)行過程主要為了掃描應用自身業(yè)務代碼以及其他第三方包中的動態(tài)特性,以便后續(xù)靜態(tài)編譯過程能順利進行,應用能正常啟動。

靜態(tài)編譯

以上步驟一切準備就緒后,通過以下命令來構建原生鏡像:

$ mvn -Pnative native:compile

成功執(zhí)行后,我們在 /target 目錄可以看到生成的可執(zhí)行文件。

程序運行

與普通可執(zhí)行文件無異,通過 target/xxx 啟動應用, 可以觀察到類似如下的輸出:

2023-08-01T17:21:21.006+08:00  INFO 65431 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-08-01T17:21:21.008+08:00  INFO 65431 --- [           main] c.a.cloud.imports.examples.Application   : Started Application in 0.553 seconds (process running for 0.562)

采用 GraalVM 靜態(tài)編譯技術的新版本 Spring Cloud Alibaba 應用,所有核心能力在啟動速度和內(nèi)存占用率方面如下表所示都有顯著改善。

圖片圖片

說明:上述測試代碼樣例來自 Spring Cloud Alibaba 項目中的 examples 模塊,4c16g Mac 環(huán)境,每組數(shù)據(jù)測試 3 次取平均,具體數(shù)據(jù)因機器不同可能會有差異。

相關鏈接:

[1] Java 程序的啟動過程分析

https://shipilev.net/talks/j1-Oct2011-21682-benchmarking.pdf

[2] GraalVM 開源高性能多語言運行時平臺

https://www.oracle.com/java/graalvm/

[3] 外部配置

https://www.graalvm.org/latest/reference-manual/native-image/metadata/

[4] AOT Engine

https://spring.io/blog/2021/12/09/new-aot-engine-brings-spring-native-to-the-next-level

[5] G1 GC

https://medium.com/graalvm/a-new-graalvm-release-and-new-free-license-4aab483692f5

[6] 動態(tài)代理類等問題

https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html#native-image.introducing-graalvm-native-images.understanding-aot-processing

[7] 相應文檔

https://medium.com/graalvm/using-graalvm-and-native-image-on-windows-10-9954dc071311

責任編輯:龐桂玉 來源: 阿里巴巴中間件
相關推薦

2017-11-22 13:01:03

Go技術棧構建

2021-12-29 08:30:48

微服務架構開發(fā)

2017-08-07 08:41:13

Java微服務構建

2024-10-10 08:34:34

事務外包模式

2022-09-05 08:00:00

Java微服務AuraDB

2020-02-17 16:28:49

開發(fā)技能代碼

2018-12-03 08:00:00

微服務gRPC

2023-06-01 15:14:55

架構Python微服務

2021-07-07 10:21:26

技術

2017-06-26 09:06:10

Spring Clou微服務架構

2020-08-14 09:27:50

微服務容器架構

2017-12-20 15:37:39

Spring Clou微服務架構

2017-09-04 16:15:44

服務網(wǎng)關架構

2018-03-26 04:53:46

Serverless微服務架構

2020-12-10 08:00:00

開發(fā).NET工具

2018-09-12 09:00:00

數(shù)據(jù)庫Redis微服務

2022-10-10 08:00:00

微服務Spring Boo容器

2022-08-22 07:26:32

Node.js微服務架構

2018-04-23 14:31:02

微服務GraphQLBFF

2024-12-23 06:10:00

點贊
收藏

51CTO技術棧公眾號