每天資訊最終,JVM還是逃不出spring的魔掌

菜單

最終,JVM還是逃不出spring的魔掌

最終,JVM還是逃不出spring的魔掌

Spring Native 簡介

我們都知道,傳統的 Spring 應用程式都是必須依賴於 Java 虛擬機器(JVM)執行的,Spring Native 的誕生就是無需 JVM,它提供了另外一種執行和部署 Spring 應用的方式(目前只支援 Java 和 Kotlin),透過 GraalVM 將 Spring 應用程式編譯成原生映象。

Spring Native 特點

1、無需 JVM 環境, Spring Native 應用程式可以作為一個可執行檔案獨立部署;

2、應用即時啟動,一般情況下應用啟動時間 < 100ms;

3、即時的峰值效能;

4、更少的記憶體消耗;

Spring Native 缺點

Spring Native 應用啟動那麼快也是有代價的,和 JVM 應用相比:

1、構建更笨重、構建時間更長;

2、更少的執行時最佳化;

3、很多 Java 功能受限;

4、很多特性還很不成熟;

Spring Native 應用場景

1、Spring Cloud 無伺服器化(Serverless);

2、以更廉價持久的方式執行 Spring 微服務;

3、非常適合 Kubernetes 平臺,如:VMware Tanzu;

4、為 Spring 應用建立更佳的容器映象;

Spring Native 和 JVM 的區別

1、Spring Native 構建時會進行應用程式靜態分析;

2、Spring Native 構建時會移除未被使用的元件;

3、Spring Native 反射、資源、動態代理需要配置化;

4、Spring Native 構建時的 classpath 是固定不變的;

5、Spring Native 沒有類延遲載入,可執行檔案包含所有內容都在啟動時載入到記憶體;

6、Spring Native 構建時會執行一些程式碼;

7、Spring Native 對於 Java 應用程式還存在一些侷限性;

GraalVM 簡介

Spring Native 的核心就是 Oracle 的黑科技: GraalVM。

GraalVM 是一個由 Oracle 開發的全棧通用虛擬機器,擁有高效能、跨語言互動等逆天特性,不僅支援了 Java、Scala、Groovy、Kotlin 等基於 JVM 的語言,以及 C、C++ 等基於 LLVM 的語言,還支援其他像 JavaScript、Ruby、Python 和 R 語言等,可提高多種語言的執行速度和吞吐量。

GraalVM 有以下幾個特性。

更加高效快速的執行程式碼

能與大多數程式語言直接互動

使用 Graal SDK 嵌入多語言

建立預編譯的原生映象

提供一系列工具來監視、除錯和配置所有程式碼

具體就不介紹了,閱讀我之前分享的這篇文章:Oracle 釋出了一個全棧虛擬機器 GraalVM

重點來看原生映象功能:

1。$ javac HelloWorld。java

2。$ time java HelloWorld

3。user 0。070s

4。$ native-image HelloWorld

5。$ time 。/helloworld

6。user 0。005s

GraalVM 可以預編譯成原生映象,從而極大提速了啟動時間,並能減少 JVM 應用的記憶體佔用。現在你知道為什麼 Spring Native 啟動那麼快的原因了!

Spring Native 正是透過 GraalVM 提供了對傳統 Spring 應用程式的輕量級執行方式,在不用修改任何傳統應用程式程式碼的情況下,透過整合 Spring Native 專案就能輕鬆實現。

開始嚐鮮

構建 Spring Native 應用的兩種方式:

1、使用 Spring Boot Buildpacks 來生成一個包含原生可執行檔案的輕量級容器;

2、使用 GraalVM native image Maven 外掛來生成一個包含原生可執行檔案;

本文使用第一種方式進行嚐鮮!

1、環境要求

這種方式需要安裝 Docker 環境:

Linux 需要配置非 root 使用者可執行

Mac 需要配置最大記憶體為 8G 或以上

2、新增依賴

Spring Native 在 start。spring。io 上面已經可以開始使用了,在頁面上新增一個 “Spring Native” 依賴進去就好,如下所示:

Spring Boot:

org。springframework。boot

spring-boot-starter-parent

2。4。5

Spring Native:

org。springframework。experimental

spring-native

${spring-native。version}

注意依賴版本:

Spring Native 最新版本為:0。9。2,只支援 Spring Boot 2。4。5

3、新增 Spring AOT 外掛

新增 Spring AOT 外掛:

org。springframework。experimental

spring-aot-maven-plugin

0。9。2

test-generate

test-generate

generate

generate

Spring AOT 外掛執行所需的提前轉換,以提升原生映象的相容性。

4、開啟原生映象支援

在 Spring Boot Maven 外掛中增加以下配置:

spring-boot-maven-plugin

paketobuildpacks/builder:tiny

true

5、新增 Maven 倉庫支援

Spring Native 依賴和外掛需要在 Spring 倉庫中下載,需要新增以下配置。

spring-release

Spring release

如果不能正常下載 Native 依賴和外掛,需要檢查 Maven 的 settings。xml 檔案:

nexus-aliyun

*,!spring-release

Nexus aliyun

把 mirrorOf 值由 * 修改為:*,!spring-release

6、新增測試介面

新增一個測試介面,原生應用啟動後,方便測試下可行性。

/**

*/

@SpringBootApplication

@RestController

public class Application {

public static void main(String[] args) {

SpringApplication。run(Application。class);

}

@RequestMapping(“/native/hi”)

@ResponseBody

public String hiNative() {

return “hi native application。。。”;

}

7、構建原生應用

Maven 外掛構建命令:

mvn spring-boot:build-image

這個會建立一個 Linux 容器,使用 GraalVM 原生映象編譯器構建出原生應用程式,容器映象預設只安裝在本地。

在 IDEA 外掛中執行:

配置好後開始構建:

會看到大量這樣的錯誤,不用理會,這個會在未來移除。

最終構建完成,一個簡單的 Spring Boot 應用程式,這個構建卻過程花了我 4 分鐘。。

8、執行原生應用

使用平常執行 Docker 映象的方式就能執行原生應用:

docker run ——rm -p 8080:8080

一般情況下,執行原生應用程式只需要 100 毫秒以下,而執行基於 JVM 的應用程式大概需要 15 秒左右。

事實是否如此呢,一起來看看!

我天,82 毫秒就啟動了,啟動確實快

再來訪問我們之前寫的介面:

輸出正常,原生應用驗證完成。

另外,在 target 目錄中也生成了可執行的 jar 包:

然後我們用傳統 JVM 環境來執行下:

java -jar spring-boot-native-1。0。jar

啟動時間:1。903 秒,雖然看起來差距不大,但原生應用啟動時間(0。082 秒)也比 JVM 快了 23 倍,在不同的程式碼量面前可能會有較大差距的體現。

當然這只是我測試的參考時間,但可以說明的原生應用執行確實要比 JVM 快不少!

我們再來比對下包的大小

檢視剛生成的 Docker 映象:

docker image ls

檢視基於 JVM 的可執行 jar 包:

Docker 映象大小:80。7 M,而基於 JVM 執行的可執行 jar 包卻只有不到 20M。

這是因為原生映象不僅包含了應用程式中所使用到的來自 JDK、Spring 中的必須項,還包含了一個最小化的 OS 系統層,所以肯定是要比之前的要大不少。

總結

本文介紹了 Spring Native 的特點,及演示了基於 Docker 映象的原生應用。

感興趣的都可以 Star 下該倉庫,包含了之前寫的 Spring Boot 教程及示例原始碼。

當然除了基於 Docker 映象,還可以使用原生映象 Maven 外掛的方式,那種方式不需要 Docker,但需要安裝原生映象編譯器 GraalVM,道理是一樣的,這裡就不再演示了

如果有使用 Docker,那第一種肯定是更好的方式,所有的依賴都打包到一個映象中了,避免了環境汙染。

最後總結一下就是,Spring Native 可以無需 JVM 執行,構建慢、啟動快、記憶體佔用少、執行最佳化少,另外還有很多 Java 特性受限,比如:反射、動態代理等都需要透過提前配置化,因為 Java 是一種動態連結的語言,原生應用都要提前編譯,這個像反射、動態代理這種特性就會受限。