Spring Boot3,啓動時間縮短 10 倍!

前面松哥寫了一篇文章和大家聊了 Spring6 中引入的新玩意 AOT(見Spring Boot3 新玩法,AOT 優化!)。

文章發出來之後,有小夥伴問松哥有沒有做性能比較,老實說,這個給落下了,所以今天再來一篇文章,和小夥伴們梳理比較小當我們利用 Native Image 的時候,Spring Boot 啓動性能從參數上來說,到底提升了多少。

> 先告訴大家結論:啓動速度提升 10 倍以上。

1. Native Image

1.1 GraalVM

不知道小夥伴們有沒有注意到,現在當我們新建一個 Spring Boot 工程的時候,再添加依賴的時候有一個 GraalVM Native Support,這個就是指提供了 GraalVM 的支持。

那麼什麼是 GraalVM 呢?

GraalVM 是一種高性能的通用虛擬機,它爲 Java 應用提供 AOT 編譯和二進制打包能力,基於 GraalVM 打出的二進制包可以實現快速啓動、具有超高性能、無需預熱時間、同時需要非常少的資源消耗,所以你把 GraalVM 當作 JVM 來用,是沒有問題的。

在運行上,GraalVM 同時支持 JIT 和 AOT 兩種模式:

  • JIT 是即時編譯(Just-In-Time Compilation)的縮寫。它是一種在程序運行時將代碼動態編譯成機器碼的技術。與傳統的靜態編譯(Ahead-of-Time Compilation)不同,靜態編譯是在程序執行之前將代碼編譯成機器碼,而 JIT 編譯器在程序運行時根據需要將代碼片段編譯成機器碼,然後再運行。所以 JIT 的啓動會比較慢,因爲編譯需要佔用運行時資源。我們平時使用 Oracle 提供的 Hotspot JVM 就屬於這種。

  • AOT 是預先編譯(Ahead-of-Time Compilation)的縮寫。它是一種在程序執行之前將代碼靜態編譯成機器碼的技術。與即時編譯(JIT)不同,即時編譯是在程序運行時動態地將代碼編譯成機器碼。AOT 編譯器在程序構建或安裝階段將代碼轉換爲機器碼,然後在運行時直接執行機器碼,而無需再進行編譯過程。這種靜態編譯的方式可以提高程序的啓動速度和執行效率,但也會增加構建和安裝的時間和複雜性。AOT 編譯器通常用於靜態語言的編譯過程,如 C、C++ 等。

如果我們在 Java 應用程序中使用了 AOT 技術,那麼我們的 Java 項目就會被直接編譯爲機器碼可以脫離 JVM 運行,運行效率也會得到很大的提升。

那麼什麼又是 Native Image 呢?

1.2 Native Image

Native Image 則是 GraalVM 提供的一個非常具有特色的打包技術,這種打包方式可以將應用程序打包爲一個可脫離 JVM 在本地操作系統上獨立運行的二進制包,這樣就省去了 JVM 加載和字節碼運行期預熱的時間,提升了程序的運行效率。

Native Image 具備以下特點:

  • 即時啓動:由於不需要 JVM 啓動和類加載過程,Native Image 可以實現快速啓動和即時執行。
  • 減少內存佔用:編譯成本地代碼後,應用程序通常會有更低的運行時內存佔用,因爲它們不需要 JVM 的額外內存開銷。
  • 靜態分析:在構建 Native Image 時,GraalVM 使用靜態分析來確定應用程序的哪些部分是必需的,並且只包含這些部分,這有助於減小最終可執行文件的大小。
  • 即時性能:雖然 JVM 可以通過JIT(Just-In-Time)編譯在運行時優化代碼,但 Native Image 提供了即時的、預先優化的性能,這對於需要快速響應的應用程序特別有用。
  • 跨平臺兼容性:Native Image 可以爲不同的操作系統構建特定的可執行文件,包括 Linux、macOS 和 Windows,即在 Mac 和 Linux 上自動生成系統可以執行的二進制文件,在 Windows 上則自動生成 exe 文件。
  • 安全性:由於 Native Image 不依賴於 JVM,因此減少了 JVM 可能存在的安全漏洞的攻擊面。
  • 與 C 語言互操作:Native Image 可以與本地 C 語言庫更容易地集成,因爲它們都是在同一環境中運行的本地代碼。

根據前面的介紹大家也能看到,GraalVM 所做的事情就是在程序運行之前,該編譯的就編譯好,這樣當程序跑起來的時候,運行效率就會高,而這一切,就是利用 AOT 來實現的。

但是!對於一些涉及到動態訪問的東西,GraalVM 似乎就有點力不從心了,原因很簡單,GraalVM 在編譯構建期間,會以 main 函數爲入口,對我們的代碼進行靜態分析,靜態分析的時候,一些無法觸達的代碼會被移除,而一些動態調用行爲,例如反射、動態代理、動態屬性、序列化、類延遲加載等,這些都需要程序真正跑起來才知道結果,這些就無法在編譯構建期間被識別出來。

而反射、動態代理、序列化等恰恰是我們 Java 日常開發中最最重要的東西,不可能我們爲了 Native Image 捨棄這些東西!因此,從 Spring6(Spring Boot3)開始支持 AOT Processing!AOT Processing 用來完成自動化的 Metadata 採集,這個採集主要就是解決反射、動態代理、動態屬性、條件註解動態計算等問題,在編譯構建期間自動採集相關的元數據信息並生成配置文件,然後將 Metadata 提供給 AOT 編譯器使用。

道理搞明白之後,接下來通過一個案例來感受下 Native Image 的威力吧!

2. 準備工作

首先需要我們安裝 GraalVM。

GraalVM 下載地址:

下載下來之後就是一個壓縮文件,解壓,然後配置一下環境變量就可以了,這個默認大家都會,我就不多說了。

GraalVM 配置好之後,還需要安裝 Native Image 工具,命令如下:

gu install native-image

裝好之後,可以通過如下命令檢查安裝結果:

另一方面,Native Image 在進行打包的時候,會用到一些 C/C++ 相關的工具,所以還需要在電腦上安裝 Visual Studio 2022,這個我們安裝社區版就行了(https://visualstudio.microsoft.com/zh-hans/downloads/):

下載後雙擊安裝就行了,安裝的時候選擇 C++ 桌面應用開發。

如此之後,準備工作就算完成了。

3. 實踐

接下來我們創建一個 Spring Boot 工程,並且引入如下兩個依賴:

然後我們開發一個接口:

@RestController
public class HelloController {

    @Autowired
    HelloService helloService;

    @GetMapping("/hello")
    public String hello() {
        return helloService.sayHello();
    }
}
@Service
public class HelloService {
    public String sayHello() {
        return "hello aot";
    }
}

這是一個很簡單的接口,接下來我們分別打包成傳統的 jar 和 Native Image。

傳統 jar 包就不用我多說了,大家執行 mvn package 即可:

mvn package

打包完成之後,我們看下耗時時間:

耗時不算很久,差不多 3.7s 左右,算是比較快了,最終打成的 jar 包大小是 18.9MB。

再來看打成原生包,執行如下命令:

mvn clean native:compile -Pnative

這個打包時間就比較久了,需要耐心等待一會:

可以看到,總共耗時 4 分 54 秒。

Native Image 打包的時候,如果我們是在 Windows 上,會自動打包成 exe 文件,如果是 Mac/Linux,則生成對應系統的可執行文件。

這裏生成的 aot_demo.exe 文件大小是 82MB。

兩種不同的打包方式,所耗費的時間完全不在一個量級。

再來看啓動時間。

先看 jar 包啓動時間:

耗時約 1.326s。

再來看 exe 文件的啓動時間:

好傢伙,只有 0.079s。

1.326/0.079=16.78

啓動效率提升了 16.78 倍!

我畫個表格對比一下這兩種打包方式:

jar Native Image
包大小 18.9MB 82MB
編譯時間 3.7s 4分54s
啓動時間 1.326s 0.079s

從這張表格中我們可以看到,Native Image 在打包的時候比較費時間,但是一旦打包成功,項目運行效率是非常高的。Native Image 很好的解決了 Java 冷啓動耗時長、Java 應用需要預熱等問題。

最後大家可以自行查看打包成 Native Image 時候的編譯結果,如下圖:

看過鬆哥之前將的 Spring 源碼分析的小夥伴,這塊的代碼應該都很好明白,這就是直接把 BeanDefinition 給解析出來了,不僅註冊了當前 Bean,也把當前 Bean 所需要的依賴給注入了,將來 Spring 執行的時候就不用再去解析 BeanDefinition 了。

同時我們可以看到在 META-INF 中生成了 reflect、resource 等配置文件。這些是我們添加的 native-maven-plugin 插件所分析出來的反射以及資源等信息,也是 Spring AOT Processing 這個環節處理的結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章