JDK 25 LTS:爆発的なパフォーマンス、極限の生産性、そして反復コードの終焉
Java 25が来た。LTS版だ。Oracleは少なくとも8年間の延長サポートを約束している。つまり、次に安心して乗れる長期基盤ということになる。
私がこのリリースで注目したのは、パフォーマンス、構文、並行性の3方向にわたる改善だ。コードを1行も変えずに速くなる部分もあれば、書き方が変わる部分もある。以下、実際に触ってみて効くと感じたものを整理する。
パフォーマンスの改善:レイテンシの削減、コスト削減
コードを変えなくても速くなる。それがこのリリースで一番嬉しいところだ。
パフォーマンス比較:JDK 21 vs. JDK 25とJava 8からの飛躍
SPECjbb2015などの標準ベンチマークを見ると、Critical JOPS(クリティカルレイテンシ)が約10%改善し、Max JOPS(最大スループット)が約5%向上している。地味に見えるかもしれないが、クラウド環境ではCPUとメモリの使用量が減る。インフラコストで20〜30%の削減につながることが多い。
まだJava 8で止まっているなら話は別だ。JDK 25に乗り換えるだけで、レイテンシが半分以下になるケースがある。リソースを3分の1に圧縮できる可能性もある。
パフォーマンスの主要機能
コンパクトオブジェクトヘッダー(JEP 519)
個人的に一番興奮したのがこれ。HotSpot VMのオブジェクトヘッダーが、96/128ビットから64ビットに縮む。小さなオブジェクトが大量に積み上がるJavaのヒープでは、これが効かないわけがない。
実際、ヒープメモリの使用量が最大22%減る。小さなオブジェクトならサイズが20%小さくなる。オブジェクトが小さくなれば、CPUのL1/L2/L3キャッシュに多く載る。キャッシュヒット率が上がれば、実行速度も上がる。地味だが、効く。
AOTメソッドプロファイリング(JEP 515)
起動時間とウォームアップの問題に手を入れた。HotSpotは以前の実行から収集したメソッドプロファイルを再利用できるようになり、JITコンパイラがプロファイルをゼロから溜め直すのを待たずに、起動直後から最適なネイティブコードを出せる。
デプロイ直後の「最初の数リクエストが遅い」が緩和されるということだ。
Java Flight Recorder(JFR)の改善
JDK 25はJFRにいくつか手を入れた。LinuxでのCPU Time Profilingがより正確になった。協調サンプリング(Cooperative Sampling)が改善され、メソッドのタイミングとトレーサビリティも上がっている。プロファイル結果を信頼できる度合いが増したのは地味に嬉しい。
開発者の生産性:より少なく書き、より多くを達成
Java 25は構文の簡素化にも踏み込んだ。ボイラープレートを減らし、コードを短く、読みやすくする方向に振っている。
まず、JEP 512のコンパクトファイル。クラス宣言もパッケージ宣言もなしにmainメソッドを書ける。スクリプト的な用途や、初心者が最初の一歩を踏み出すのに良い。
Switchでのパターンマッチングはrecordsの直接分解に対応した。カンマ区切りで複数のcaseをグループ化でき、匿名パターン_で使わない変数やrecordのコンポーネントを明示的に無視できる。これらはコードを短くするだけでなく、意図を明確にする。
JEP 511のモジュールインポート宣言(import module java.base;)も地味に効く。import文の羅列が減る。さらに、patterns、instanceof、switchでプリミティブ型を扱えるようになった(第3プレビュー、JEP 507)。型システムの一貫性が上がる。
並行性と安定性:Null-Safeの未来
JDK 21でVirtual Threadsが入った。Java 25はその上に乗る道具を整えた。
Scoped Values(JEP 506):ThreadLocalの後継
ThreadLocalに代わるScoped Valuesが、Virtual Threadsと組み合わせて安全に使えるよう設計されている。値が一度定義されたら不変で、ScopedValue.where(…).run(…)のスコープ内でのみ存在する。スコープを抜ければ消えるので、ThreadLocalで悩まされてきたメモリリークの心配がない。
Virtual Threadsについて一つ注意がある。データベース集約的な負荷では、接続プールがボトルネックになって常に性能向上するとは限らない。だが、開発者の生産性という観点では大きい。リアクティブプログラミングの複雑さを捨てて、シンプルなThread-per-Requestモデルに戻れるからだ。
構造化並行性(Structured Concurrency、第5プレビュー)(JEP 505)
異なるスレッドで走る関連タスクを、一つの作業単位として扱うAPIだ。エラー処理とキャンセルが自然に伝播する。並行コードの観察可能性も上がる。まだプレビューだが、並行処理を書く人の苦労をかなり減らしてくれそうだ。
安定性のボーナストラック:JSpecify
JEP直接の機能ではないが、Spring Framework 7などがJSpecify(nullabilityのオープン標準)を採用し始めたのは大きな動きだ。IDEや静的解析ツールが「この値はnullになりうるか」を統一的に理解できるようになる。実行時にNullPointerExceptionで死ぬ世界から、コンパイル時にそれを防ぐ世界へ少しずつ進んでいる。
JDK 25の追加改善
コンパイラとランタイムの改善
この他にも、JITコンパイラの最適化、ガベージコレクターのポーズ時間削減、JVMランタイムの細かい改善が入っている。地味だが、積み重なれば効く。
JDKライブラリの改善
JDKライブラリにも機能面とパフォーマンス面で細かな改善が入っている。リリースノートを一通り眺めて、自分の使っているAPIに影響がないか確認したい。
移行に関する考慮事項
移行を考えるなら、3点に注意したい。
まずLTSサポート。Java 25は少なくとも8年サポートされるので、大規模移行のサイクルを伸ばせる。
次に、32ビットx86のサポートが終了する。すべての環境が64ビットであることを確認すること。
最後に、Security Managerが永久に無効化された。将来のバージョンでAPIごと削除される。モジュールシステムやコンテナセキュリティに基づく現代的なセキュリティモデルへの移行を進める必要がある。
パフォーマンス改善を理解するためのメタファー
図書館に例えるとわかりやすい。これまでのJavaは、本1冊(オブジェクト)ごとに大きな索引カード(96/128ビットのヘッダー)を付けて棚に並べていた。Compact Object Headersは、そのカードをQRコードサイズ(64ビット)に縮めた。同じ棚により多くの本が収まり、見つけるのも速くなる。
結論
JDK 25は、メモリ効率の改善、起動の高速化、構文の簡素化が揃った。どれも派手ではないが、日々の開発と運用コストに直結する。
Oracleが8年サポートを約束した以上、これを基盤に据える理由は十分だ。AIと高並行性の時代に向けて、Javaがちゃんと準備している。私としては、可能なら早めに移行しておきたい。