1. 序章

このチュートリアルでは、Spring Bootの起動時間を短縮するのに役立つさまざまな構成とセットアップについて説明します。 まず、Spring固有の構成について説明します。 次に、Java仮想マシンのオプションについて説明します。 最後に、GraalVMとネイティブイメージのコンパイルを活用して起動時間をさらに短縮する方法について説明します。

2. 春の微調整

始める前に、テストアプリケーションを設定しましょう。 依存関係としてSpringWeb、Spring Actuator、およびSpringSecurityを備えたSpring Bootバージョン2.5.4を使用します。 pom.xmlに、 spring -boot-maven-plugin を追加して、アプリケーションをjarファイルにパックするように構成します。

<plugin> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-maven-plugin</artifactId> 
    <version>${spring-boot.version}</version> 
    <configuration> 
        <finalName>springStartupApp</finalName> 
        <mainClass>com.baeldung.springStart.SpringStartApplication</mainClass> 
    </configuration> 
    <executions> 
        <execution> 
            <goals> 
                <goal>repackage</goal> 
            </goals> 
        </execution> 
    </executions> 
</plugin>

標準のjava-jar コマンドを使用してjarファイルを実行し、アプリケーションの開始時刻を監視します。

c.b.springStart.SpringStartApplication   : Started SpringStartApplication in 3.403 seconds (JVM running for 3.961)

ご覧のとおり、アプリケーションは約3.4秒で開始されます。 今回は、今後の調整の参考として使用します。

2.1. 怠惰な初期化

Springフレームワークは遅延初期化をサポートしています。 遅延初期化とは、Springが起動時にすべてのBeanを作成しないことを意味します。 また、Springは、そのBeanが必要になるまで依存関係を注入しません。 SpringBootバージョン2.2以降。 application.properties を使用して、遅延初期化を有効にすることができます。

spring.main.lazy-initialization=true

前の例のように新しいjarファイルを作成して開始した後、新しい起動時間は少し良くなります。

 c.b.springStart.SpringStartApplication   : Started SpringStartApplication in 2.95 seconds (JVM running for 3.497)

コードベースのサイズによっては、遅延初期化によって起動時間が大幅に短縮される可能性があります。短縮は、アプリケーションの依存関係グラフによって異なります。

また、遅延初期化には、DevToolsホットリスタート機能を使用している間の開発中に利点があります。 遅延初期化による再起動の回数が増えると、JVMはコードをより適切に最適化できるようになります。

ただし、遅延初期化にはいくつかの欠点があります。 最も重大な欠点は、アプリケーションが最初のリクエストを処理するのが遅くなることです。 Springは必要なBeanを初期化するのに時間がかかるため、もう1つの欠点は、起動時にいくつかのエラーを見逃す可能性があることです。 これにより、実行時にClassNotFoundExceptionが発生する可能性があります。

2.2. 不要な自動設定を除く

Spring Bootは、設定より規約を常に優先します。 Springは、アプリケーションが必要としないBeanを初期化する場合があります。起動ログを使用して、自動構成されたすべてのBeanを確認できます。 application.propertiesorg.springframework.boot.autoconfigureでログレベルをDEBUGに設定します。

logging.level.org.springframework.boot.autoconfigure=DEBUG

ログには、自動構成専用の新しい行が表示されます。

============================
CONDITIONS EVALUATION REPORT
============================

このレポートを使用して、アプリケーションの構成の一部を除外できます。 構成の一部を除外するには、@EnableAutoConfigurationアノテーションを使用します。

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

Jackson JSONライブラリと、使用しないメトリック構成の一部を除外すると、起動にかかる時間を節約できます。

c.b.springStart.SpringStartApplication   : Started SpringStartApplication in 3.183 seconds (JVM running for 3.732)

2.3. その他のマイナーな調整

Spring Bootには、サーブレットコンテナが組み込まれています。 デフォルトでは、Tomcatを取得します。 ほとんどの場合、Tomcatで十分ですが、他のサーブレットコンテナの方がパフォーマンスが高くなる可能性がありますテストでは、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>

Springコンポーネントは1つしかないため、この調整ではテストで有意な結果は得られませんでした。

次に、application.properties(または.yml)ファイルの有効な場所がいくつかあります。 ほとんどの場合、クラスパスルートまたはjarファイルと同じフォルダにあります。 spring.config.location パラメーターを使用して明示的なパスを設定することで、複数の場所の検索を回避し、検索にかかるミリ秒を数ミリ秒節約できます。

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

最後に、Spring Bootは、JMXを使用してアプリケーションを監視するためのいくつかのMBeanを提供します。 JMXを完全にオフにして、それらのBeanを作成するコストを回避します:

spring.jmx.enabled=false

3. JVMの微調整

3.1. Verifyフラグ

このフラグは、バイトコードベリファイアモードを設定します。 バイトコード検証は、クラスが適切にフォーマットされ、JVM仕様の制約内にあるかどうかを提供します。 起動時にJVMにこのフラグを設定します。

このフラグにはいくつかのオプションがあります。

  • -Xverify はデフォルト値であり、すべての非ブートローダークラスで検証を有効にします。 
  • -Xverify:allは、すべてのクラスの検証を有効にします。この設定は、スタートアップに重大な悪影響を及ぼします。
  • -Xverify:none(または-Xnoverify)。このオプションは、ベリファイアを完全に無効にし、起動時間を大幅に短縮します。

起動時にこのフラグを渡すことができます:

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

このオプションは非推奨であるという警告がJVMから届きます。 また、起動時間が短縮されます。

 c.b.springStart.SpringStartApplication   : Started SpringStartApplication in 3.193 seconds (JVM running for 3.686)

このフラグは、重大なトレードオフをもたらします。 私たちのアプリケーションは、実行中に壊れて、以前にキャッチできたエラーが発生する可能性があります。 これが、このオプションがJava13で非推奨としてマークされている理由の1つです。 したがって、将来のリリースで削除される予定です。

3.2. TieredCompilationフラグ

Java 7では、階層型コンパイルが導入されました。 HotSpotコンパイラは、コードに対してさまざまなレベルのコンパイルを使用します。

ご存知のように、Javaコードは最初にバイトコードに解釈されます。 次に、バイトコードがマシンコードにコンパイルされます。 この変換はメソッドレベルで行われます。 C1コンパイラは、一定量の呼び出しの後にメソッドをコンパイルします。 さらに実行すると、C2コンパイラはコンパイルしてパフォーマンスをさらに向上させます。

-XX:-TieredCompilationフラグを使用すると、中間コンパイル層を無効にできます。これは、メソッドが解釈されるか、C2コンパイラでコンパイルされて最大の最適化が行われることを意味します。 これにより、起動速度が低下することはありません。 必要なのは、C2コンパイルを無効にすることです。 これは、 -XX:TieredStopAtLevel =1オプションで実行できます。 -noverify フラグと組み合わせて、これにより起動時間を短縮できます。 残念ながら、これにより後の段階でJITコンパイラの速度が低下します。

TieredCompilationフラグだけでも、確実な改善がもたらされます。

 c.b.springStart.SpringStartApplication   : Started SpringStartApplication in 2.754 seconds (JVM running for 3.172)

追加のキックとして、このセクションの両方のフラグを組み合わせて実行すると、起動時間がさらに短縮されます。

 java -jar -XX:TieredStopAtLevel=1 -noverify .\target\springStartupApp.jar
c.b.springStart.SpringStartApplication : Started SpringStartApplication in 2.537 seconds (JVM running for 2.912)

4. 春のネイティブ

ネイティブイメージは、事前コンパイラを使用してコンパイルされ、実行可能ファイルにパックされたJavaコードです。 Javaを実行する必要はありません。 結果として得られるプログラムは、JVMのオーバーヘッドがないため、より高速でメモリ依存性が低くなります。 GraalVM プロジェクトでは、ネイティブイメージと必要なビルドツールが導入されました。

Spring Nativeは、GraalVMネイティブイメージコンパイラを使用したSpringアプリケーションのネイティブコンパイルをサポートする実験的なモジュールです。事前コンパイラは、ビルド時にいくつかのタスクを実行して、起動時間を短縮します(静的分析、削除未使用のコードの作成、固定クラスパスの作成など)。 ネイティブイメージにはまだいくつかの制限があります。

  • すべてのJava機能をサポートしているわけではありません
  • リフレクションには特別な構成が必要です
  • レイジークラスの読み込みは利用できません
  • Windowsの互換性が問題です。

アプリケーションをネイティブイメージにコンパイルするには、を追加する必要があります春-aot spring-aot-maven-plugin への依存 pom.xml。 Mavenはネイティブイメージを作成しますパッケージのコマンド目標フォルダ。

5. 結論

この記事では、SpringBootアプリケーションの起動時間を改善するためのさまざまな方法について説明しました。 最初に、起動時間を短縮するのに役立つさまざまなSpring関連の機能について説明しました。 次に、JVM固有のオプションを示しました。 最後に、SpringNativeとネイティブイメージの作成について紹介しました。 いつものように、この記事で使用されているコードは、GitHubにあります。