加快Spring Boot啟動的幾種方法 | baeldung

banq發表於2021-12-20

在本教程中,我們將介紹有助於減少 Spring Boot 啟動時間的不同配置和設定:
  • 首先,我們將討論 Spring 特定的配置。
  • 其次,我們將介紹 Java 虛擬機器選項。
  • 最後,我們將介紹如何利用 GraalVM 和本機映象編譯來進一步縮短啟動時間。

 

延遲初始化
Spring Framework 支援延遲初始化。延遲初始化意味著 Spring 不會在啟動時建立所有 bean。此外,在需要該 bean 之前,Spring 不會注入任何依賴項。從 Spring Boot 2.2 版開始。可以使用application.properties啟用延遲初始化:
spring.main.lazy-initialization=true
根據我們程式碼庫的大小情況,延遲初始化甚至會導致很大的啟動時間減少。這種減少取決於我們應用程式的依賴關係圖。
此外,延遲初始化在使用 DevTools 熱重啟功能的開發過程中也有好處。使用延遲初始化增加重啟次數將使 JVM 能夠更好地最佳化程式碼。
但是,延遲初始化有一些缺點。最顯著的缺點是應用程式會較慢地處理第一個請求。因為 Spring 需要時間來初始化所需的 bean,另一個缺點是我們可能會在啟動時遺漏一些錯誤。這可能會在執行時導致ClassNotFoundException 。
 

排除不必要的自動配置
Spring Boot 總是喜歡約定而不是配置。Spring 可能會初始化我們的應用程式不需要的 bean。我們可以使用啟動日誌檢查所有自動配置的 bean。在application.properties 中的org.springframework.boot.autoconfigure上將日誌記錄級別設定為 DEBUG :
logging.level.org.springframework.boot.autoconfigure=DEBUG
在日誌中,我們將看到專用於自動配置的新行,然後根據這些輸出,我們可以排除這些應用程式配置。
為了排除部分配置,我們使用@EnableAutoConfiguration註解:

@EnableAutoConfiguration(exclude = {JacksonAutoConfiguration.class, JvmMetricsAutoConfiguration.class, 
  LogbackMetricsAutoConfiguration.class, MetricsAutoConfiguration.class})

如果我們排除 Jackson JSON 庫和一些我們不使用的指標配置,我們可以在啟動時節省一些時間。
  

切換到 Undertow
Spring Boot 帶有一個嵌入式 servlet 容器。預設情況下,我們得到 Tomcat。雖然 Tomcat 在大多數情況下已經足夠好,但其他 servlet 容器的效能可能更高。在測試中,來自 JBoss 的 Undertow 的效能優於 Tomcat 或 Jetty。它需要更少的記憶體並具有更好的平均響應時間。要切換到 Undertow,我們需要更改pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

 

生成索引
Spring 類路徑掃描是快速操作。當我們擁有大型程式碼庫時,我們可以透過建立靜態索引來縮短啟動時間。我們需要給spring-context-indexer新增一個依賴來生成索引。Spring 不需要任何額外的配置。在編譯時,Spring 將在META-INF\spring.components 中建立一個附加檔案。Spring 會在啟動時自動使用它:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-indexer</artifactId>
    <version>${spring.version}</version>
    <optional>true</optional>
</dependency>

 

搜尋多個位置
application.properties(或 .yml)檔案有幾個有效的位置。最常見的是在類路徑根目錄或與 jar 檔案相同的資料夾中。我們可以透過使用spring.config.location引數設定顯式路徑來避免搜尋多個位置,並在搜尋時節省幾毫秒:

java -jar .\target\springStartupApp.jar --spring.config.location=classpath:/application.properties

最後,Spring Boot 提供了一些 MBean 來使用 JMX 監控我們的應用程式。完全關閉 JMX 並避免建立這些 bean 的成本:
spring.jmx.enabled=false
 

JVM的Verify調整
此標誌設定位元組碼驗證器模式。位元組碼驗證提供類的格式是否正確以及是否在 JVM 規範約束內。我們在啟動期間在 JVM 上設定了這個標誌。
Verify標誌有幾個選項:

  • -Xverify是預設值並啟用對所有非引導載入程式類的驗證。 
  • -Xverify:all啟用對所有類的驗證。此設定將對初創公司產生顯著的負面效能影響。
  • -Xverify:none(或-Xnoverify)。此選項將完全禁用驗證程式並將顯著減少啟動時間。

我們可以在啟動時傳遞這個標誌:

java -jar -noverify .\target\springStartupApp.jar 


我們將收到來自 JVM 的警告,指出該選項已被棄用。但是啟動時間減少了。
這個標誌帶來了重要的權衡。我們的應用程式可能會在執行時因我們可以更早捕獲的錯誤而中斷。這是該選項在 Java 13 中被標記為已棄用的原因之一。因此它將在未來版本中刪除。
  

JVM分層編譯標誌
Java 7 引入了分層編譯。HotSpot 編譯器將對程式碼使用不同級別的編譯。
眾所周知,Java 程式碼首先被解釋為位元組碼。接下來,位元組碼被編譯成機器碼。這種轉換髮生在方法級別。C1 編譯器在一定數量的呼叫後編譯一個方法。執行更多次之後,C2 編譯器會編譯它,從而進一步提高效能。
使用-XX:-TieredCompilation標誌,我們可以禁用中間編譯層。這意味著我們的方法將使用 C2 編譯器進行解釋或編譯,以實現最大程度的最佳化。這不會導致啟動速度下降。我們需要的是禁用 C2 編譯。我們可以使用-XX:TieredStopAtLevel=1選項來做到這一點。結合-noverify標誌,這可以減少啟動時間。不幸的是,這會在後期減慢 JIT 編譯器的速度。
  

Spring Native
本機映像/映象(Image)是使用提前編譯器編譯並打包成可執行檔案的 Java 程式碼。它不需要Java來執行。由於沒有 JVM 開銷,因此生成的程式速度更快,對記憶體的依賴更少。該GraalVM專案介紹本機映像和所需的構建工具。

Spring Native是一個實驗性模組,支援使用 GraalVM 原生映象編譯器對 Spring 應用程式進行原生編譯。提前編譯器在構建期間執行多項任務以減少啟動時間(靜態分析、刪除未使用的程式碼、建立固定類路徑等)。原生映象仍然有一些限制:

  • 它不支援所有 Java 功能
  • 反射需要特殊的配置
  • 懶載入類不可用
  • Windows 相容性是一個問題。

要將應用程式編譯為原生映像,我們需要將spring-aot 和spring-aot-maven-plugin依賴項新增到pom.xml。Maven 將在目標資料夾中的package命令上建立本機映像。

相關文章