接著我們回到前幾章介紹的 Hello Quarkus 專案,這次我們將其執行在 GraalVM 環境下。如果你使用的是 Windows 系統,設置起來可能會稍微複雜一些,也可能會遇到各種問題。這裡建議使用 WSL2(Windows Subsystem for Linux 2) 來運行該專案,能夠簡化過程並減少問題。關於 WSL2 的安裝,可以參考 微軟官方文件。
在前幾章中我們已經介紹過環境設定,這裡我們將構建指令修改如下,讓專案運行在 Native Image 模式下:
./gradlew clean build '-Dquarkus.package.type=native'
如果尚未安裝 GraalVM 的 Native Image 工具,請使用以下指令安裝:
gu install native-image
接著,確認 native-image 是否安裝成功,可以用以下指令檢查版本:
native-image --version
專案建置完成後,終端機會顯示一些構建資訊,如下圖所示。這些訊息包含了性能優化建議,能幫助我們進一步提升效能:
Top 10 Origins of Code Area 和 Top 10 Object Types in Image Heap:這兩部分展示了生成原生映像過程中,程式碼區域和記憶體堆中的占用情況。例如,像 java.base
和 io.netty
這些常用的類庫佔用較多的空間。
Recommendations: 系統將提供兩個主要的優化建議:
march=native
這個選項來啟用更多的 CPU 特性,進而提升運行效率。Produced Artifacts: 顯示構建過程生成的工件,包含 build-info
檔案和可執行文件。
專案構建完成後,可以到 build
資料夾下,執行生成的可執行檔,例如:
./hello-quarkus-1.0-SNAPSHOT-runner
成功啟動後,你會看到類似以下的結果畫面。
接著,我們來稍微比較一下 JVM 和 GraalVM 在啟動時間上的差異:
可以看出,GraalVM Native Image 的啟動速度大幅快於 JVM。
在生成 Native Image 時,雖然啟動時間顯著減少,但我們仍然需要根據應用程式的具體需求進行一些調整,以平衡效能和資源消耗。以下幾個參數可以幫助我們進一步優化 Native Image 的執行效能:
./hello-quarkus-1.0-SNAPSHOT-runner -Xmx256m
這樣可以防止應用程式過度佔用系統資源,確保穩定性。
--report-unsupported-elements-at-runtime
和 --no-fallback
來優化應用程式的二進制文件。./gradlew build -Dquarkus.native.additional-build-args="--no-fallback --report-unsupported-elements-at-runtime"
使用這些選項可以有效減少最終生成的二進制文件大小,進一步提升效能。
@RegisterForReflection
註解,告訴 GraalVM 哪些類需要保留反射支援,這樣可以避免過度的資源消耗。@RegisterForReflection
public class MyEntity {
// fields and methods
}
在生產環境中,將 Native Image 包裝成 Docker 容器是常見的做法。以下是如何構建一個輕量級的 Docker 映像:
首先,在 Dockerfile
中使用 GraalVM 提供的基底映像,並將構建出的 Native Image 包裝其中:
FROM graalvm-ce:latest AS build
COPY target/hello-quarkus-1.0-SNAPSHOT-runner /app/hello-quarkus
CMD ["./app/hello-quarkus"]
執行以下指令來生成 Docker 映像並啟動容器:
docker build -t hello-quarkus-native .
docker run --rm -p 8080:8080 hello-quarkus-native
使用 Docker 部署 Native Image,可以進一步提高應用程式的可攜性和隔離性,適合在微服務架構中使用。
雖然 GraalVM Native Image 是透過 預先編譯(Ahead-of-Time, AOT) 將 Java 應用程式轉換為原生執行檔案,但它仍然保留了一些 JVM 的設定選項,特別是與記憶體管理相關的參數。在這一章節中,我們將討論一些常見的 JVM 參數如何在 Native Image 中運作,以及這些設定對應用程式效能和資源使用的影響。
-Xmx
和 -Xms
記憶體限制參數Xmx
和 Xms
是最常用的 JVM 參數之一,用來控制應用程式執行時的最大堆積記憶體與初始記憶體大小。在 GraalVM 的 Native Image 中,這兩個參數仍然有效,並可用來調整應用程式的記憶體分配。
Xmx
:設定應用程式執行時的最大堆積記憶體大小。範例如下:
./hello-quarkus-1.0-SNAPSHOT-runner -Xmx256m
上述指令限制應用程式最多使用 256 MB 的記憶體,這對於生產環境中記憶體資源有限的情況(例如容器化部署)非常有用。
Xms
:設定應用程式啟動時的初始堆積記憶體大小。範例如下:
./hello-quarkus-1.0-SNAPSHOT-runner -Xms128m
這樣可以在應用程式啟動時,直接分配 128 MB 的記憶體,有助於提升啟動效能,特別是在應用程式需要立即使用較大記憶體的情況下。
除了 -Xmx
和 -Xms
之外,還有一些記憶體管理相關的 JVM 參數可以在 Native Image 中使用:
XX:MaxDirectMemorySize
:設定直接記憶體的最大值。這個參數對於使用 NIO 或 Netty 這類框架的應用程式非常重要,因為它們大量使用直接記憶體。範例如下:
./hello-quarkus-1.0-SNAPSHOT-runner -XX:MaxDirectMemorySize=128m
這樣可以限制應用程式最多使用 128 MB 的直接記憶體。
XX:+UseG1GC
:雖然 Native Image 不再依賴傳統的 JVM 垃圾回收機制,但在某些情況下仍然可以啟用特定的垃圾回收策略來進行記憶體管理。然而,這在 Native Image 中較少見。
在 GraalVM 中,Native Image 是透過 AOT 編譯產生的原生執行檔案,理論上不需要 JVM 層級的運行環境。然而,為了提供與傳統 JVM 模式相似的調整能力,GraalVM 仍然允許使用部分 JVM 參數來控制堆積記憶體大小、直接記憶體及垃圾回收等功能。這使開發者能夠無縫地從 JVM 運行模式過渡到 Native Image 模式,並能精確控制應用程式的資源使用。
大多數應用程式的記憶體管理是關鍵因素之一。在高效能的生產環境中,我們希望應用程式能夠在固定的資源配置下運行,且不會因記憶體不足而崩潰。這就是為何在構建和運行 Native Image 時,我們仍需調整記憶體參數來滿足不同場景需求。常見的應用場景包括:
Xmx
,可以限制每個服務的記憶體使用,確保系統整體資源不會被某個服務過度佔用。Xmx
和 Xms
來限制記憶體並加快啟動速度。Xmx
來限制記憶體使用,避免系統過載。在前面我們介紹了如何使用 GraalVM 在 Quarkus 專案中生成並執行 Native Image,雖然 Native Image 提供了優秀的效能,但對於開發者來說,這種模式並不如 JVM 那麼友善。
首先,開發工具的支援仍不夠完善。許多 Java 開發時常用的工具與框架在 Native Image 的環境下並沒有完整的支援,這讓開發過程變得更複雜。其次,編譯時間較長 是另一個顯著的挑戰。Native Image 的生成過程需要進行繁瑣的靜態編譯,這相較於 JVM 下的即時編譯(JIT)而言,開發者在每次構建或調試時都必須等待更長的時間,這可能會延長開發週期。此外,Native Image 中一些常見的動態功能(如反射和動態類加載)需要手動配置,這進一步增加了開發難度。
因此,雖然 Native Image 提供了更好的運行效能,但在開發階段,開發者往往需要面對工具不足、長編譯時間以及更多的配置工作。
尤其是windows系統,設置上遇到蠻多問題點的,所以建議可以在WSL2去實驗這些。也不用太糾結一定要在Windows下做實驗,因為後續做是做容器服務。
這裡總結一些常用指令,特別針對 GraalVM 和 Native Image 開發環境,讓大家更方便運行和測試專案:
構建 Native Image:使用 GraalVM 建構專案為 Native Image:
./gradlew clean build '-Dquarkus.package.type=native'
啟動 Native Image:運行建構完成的 Quarkus 應用:
./your-project-name-runner
開發模式(JVM 模式):在 JVM 模式下開發,支援即時重載:
./gradlew quarkusDev
清理專案:移除 build
和 target
資料夾中的建構檔案:
./gradlew clean
構建 JVM 模式專案:建構專案為可執行的 JAR 檔案:
./gradlew build
運行測試:運行專案測試,確保代碼通過所有測試:
./gradlew test
列出所有 Gradle 任務:查看專案中所有可用的 Gradle 任務:
./gradlew tasks
運行 Native Image 測試:在 Native Image 環境中運行測試:
./gradlew test '-Dquarkus.package.type=native'
啟用文件系統監控:增量建構時自動檢測文件變更:
./gradlew clean build '-Dquarkus.package.type=native' -Dorg.gradle.vfs.watch=true