1同じアプリを4通りの形で起動する
のまま、置き方と事前の準備だけを変えた実測です(ready はプロセスの起動直前から GET /hello の応答を読み終えるまで)。起動は段階ごとに短くなりましたが、 は を使っても減っていません。下の は の再起動をはさんだ別の系列なので、表を分けてあります。
再起動前の系列形態 Started in(秒) ready(ms) アイドル RSS(MB)fat JAR(java -jar demo.jar) 0.599〜0.637 814〜844 266〜285展開(java -jar application/demo.jar) 0.534〜0.551 695〜716 265〜287展開 + CDS 0.354〜0.370 468〜492 264〜306展開 + AOT キャッシュ 0.252〜0.259 366〜441 288〜304再起動後の系列(-Pnative でビルドした JAR を JVM で -Dspring.aot.enabled=true)形態 Started in(秒) ready(ms) アイドル RSS(MB)展開(取り直し) 0.530〜0.642 691〜804 268〜287展開 + AOT キャッシュ(取り直し) 0.247〜0.445 359〜584 280〜298展開 + Spring AOT 0.423〜0.460 570〜610 251〜260展開 + Spring AOT + AOT キャッシュ 0.176〜0.204 278〜377 229〜243
2fat JAR のまま AOT キャッシュを指定したときの警告
展開せずに へ を付けて起動したときに JVM が出した行です。キャッシュを作ったときとが合わないと言って、キャッシュ無しで起動しました(起動時間は 0.610〜0.639 秒で、付けないときの 0.599〜0.637 秒とほぼ同じです)。リファレンスにも「You have to use the cache file with the extracted form of the application, otherwise it has no effect.」とあります。
[0.014s][warning][aot] This file is not the one used while building the AOT cache: 'demo.jar', size has changed[0.014s][error ][aot] An error has occurred while processing the AOT cache. Run with -Xlog:aot for details.[0.014s][error ][aot] shared class paths mismatch (hint: enable -Xlog:class+path=info to diagnose the failure)[0.015s][error ][aot] Unable to map shared spaces...
3素の Java で見る: 名前で呼ぶクラスはソースを読んでも分からない
は、アプリを動かさずにコードを解析して、要るだけを実行ファイルに入れます。ところが下の2つ目の呼び出しのように、クラス名を実行時に組み立ててで呼ぶと、ソースを読むだけではどのクラスが要るか決まりません。Spring AOT は の生成を HelloController::new のような普通のコードに置き換え、それでも残るリフレクションの対象は一覧にして渡します。
4Spring AOT が出力した申告の一覧
がビルド時に生成した の冒頭です(全体は 147,945 バイト)。リフレクションで触るクラスを1つずつ並べて、native image の解析に伝えています。なお 4.1 のリファレンスは reflect-config.json など複数のファイル名を挙げていますが、4.1.1 が実際に生成したのはこの1本と native-image.properties でした。
{"comment": "Spring Framework 7.0.9","reflection": [{"type": "ch.qos.logback.classic.LoggerContext"},{"type": "ch.qos.logback.classic.pattern.SyslogStartConverter","allPublicConstructors": true},...
5native image のビルドログ: 1分あまりと約4GBの中身
のログのうち、 が出した部分の抜粋です。hello を返すだけのアプリで、到達できると判定された型が 18,730、リフレクション用に登録された型が 7,026 ありました。この回のピークのメモリは 4.18GB で、同じプロジェクトの は 2.3 秒でした。
[1/8] Initializing... (3.2s @ 0.17GB)C compiler: gcc (redhat, aarch64, 14.3.1)Garbage collector: Serial GC (max heap size: 80% of RAM)[2/8] Performing analysis... [*****] (18.5s @ 1.85GB)18,730 types, 27,825 fields, and 87,422 methods found reachable7,026 types, 3,724 fields, and 15,151 methods registered for reflection[3/8] Building universe... (4.1s @ 2.12GB)[4/8] Parsing methods... [**] (2.5s @ 2.33GB)[5/8] Inlining methods... [***] (1.5s @ 2.59GB)[6/8] Compiling methods... [*****] (23.5s @ 2.21GB)[7/8] Laying out methods... [***] (5.1s @ 2.89GB)[8/8] Creating image... [**] (3.2s @ 3.24GB)43.18MB (47.14%) for code area: 57,689 compilation units43.06MB (47.01%) for image heap: 399,894 objects and 828 resources91.59MB in total image size, 86.71MB in total file size6.4s (10.0% of total time) in 1114 GCs | Peak RSS: 4.18GB | CPU load: 6.99Finished generating 'demo' in 1m 3s.