Java 21(2023年9月GA)とJava 25(2025年9月16日GA)は、どちらも長期サポート(LTS)版です。違いは、間にある非LTSの22・23・24と25の4リリースで正式化した機能の差です。この記事では、その一覧と両者のサポート期限を並べ、Temurin 21.0.12.1とTemurin 25.0.4.1で同じコードを動かして挙動の差を確かめた結果を載せます。2026年10月時点の最新版は非LTSのJava 27です(Java 27の変更点は別記事で扱います)。
まとめ:Java 21と25の違いと移行判断の要点
- 言語:無名変数(22)、柔軟なコンストラクタ本体・モジュールimport・コンパクトソース(いずれも25)が正式化しました。Java 21ではプレビュー扱いか未搭載です。
- 実行時:
synchronized内で仮想スレッドがキャリアを占有する問題(pinning)が24で解消しました。macOS x86_64・16論理CPUで、各タスクが別々のロックを持つ掲載サンプルを計測すると、1,000本の仮想スレッドの完了時間は約14〜15秒から0.3秒前後になりました。 - 互換性:Security Managerは24で恒久無効化され、
-Djava.security.manager=allowを付けた起動スクリプトは25ではJVMが起動しません。sun.misc.UnsafeのメモリアクセスとJNIは、25で標準エラーに警告を出します。 - サポート:Oracle JDK 21は2026年10月のCPUから無償ライセンス(NFTC)の対象外になり、Java SE OTNライセンスでの提供に切り替わります。Oracle JDK 21の更新を本番で使い続ける場合は、OTNの許諾用途や既存契約の対象かを確認し、対象外なら25またはOpenJDKビルドへの切り替え、もしくは商用ライセンスの取得が必要です。
- 判断:新規開発は25を選びます。21に残るのは、Security Managerやフレームワーク側の対応版が移行を止めている場合に限ります。
Java 21と25の位置づけ:中間リリースと現行版27の比較
Javaは半年ごとに新版を出し、2年ごとの9月版をLTSにしています。21の次のLTSが25で、22・23・24は半年でOracleのPremier Supportが終わる非LTSでした。OpenJDKのJDK 25プロジェクトページは、25がほとんどのベンダーでLTSになると説明しています。
| 版 | GA | 種別 | クラスファイル版 |
|---|---|---|---|
| Java 21 | 2023年9月 | LTS | 65 |
| Java 22 | 2024年3月19日 | 非LTS | 66 |
| Java 23 | 2024年9月17日 | 非LTS | 67 |
| Java 24 | 2025年3月18日 | 非LTS | 68 |
| Java 25 | 2025年9月16日 | LTS | 69 |
| Java 27 | 2026年9月15日 | 非LTS(現行) | 71 |
クラスファイル版は、ビルドと実行のJDKがずれたときのエラー文に出る数字です。JDK 25のjavacで既定のまま出力したクラスをJava 21で動かすと、次のように失敗しました。
java.lang.UnsupportedClassVersionError: Cpu has been compiled by a more recent version of the Java Runtime (class file version 69.0), this version of the Java Runtime only recognizes class file versions up to 65.0
実行環境を21に残したままビルドだけ25へ上げるなら、javac --release 21 を指定します。この指定で出力したクラスは21で動きました。次のLTSはJava 29(2027年9月予定)で、22〜24の各版の詳細はJava 24の新機能やJava 23の新機能にまとめています。
Java 22〜25の主な正式機能と互換性変更
21から25へ上げると、次の主なJEPが正式に入ります。削除や既定値の変更も含め、プレビュー・インキュベーター・実験的機能は除いています(出典は各版の OpenJDKプロジェクトページ)。
| 版 | 分類 | 正式化したJEP |
|---|---|---|
| 22 | 言語 | 456 無名変数とパターン |
| 22 | API・ツール | 454 FFM API、458 複数ファイルのソース起動 |
| 22 | GC | 423 G1のリージョンピン留め |
| 23 | ツール | 467 Markdownのドキュメントコメント |
| 23 | GC | 474 ZGCの世代別モードを既定化 |
| 24 | API | 484 Class-File API、485 Stream Gatherers |
| 24 | 暗号 | 496 ML-KEM、497 ML-DSA |
| 24 | 実行時 | 483 AOTクラス読み込み、491 pinning解消 |
| 24 | 削除・制限 | 486 Security Manager恒久無効化、490 ZGC非世代モード削除 |
| 25 | 言語 | 511 モジュールimport、512 コンパクトソース、513 柔軟なコンストラクタ |
| 25 | API | 506 Scoped Values、510 鍵導出関数API |
| 25 | 実行時 | 514・515 AOT、519 Compact Object Headers、521 世代別Shenandoah |
| 25 | JFR | 518 協調サンプリング、520 メソッド計時 |
言語機能:21で落ちて25で通るコード
差が一番わかりやすいのは書き方です。クラス宣言と static を省いた次のファイルは、25では java Hello.java でそのまま動きます。
void main() {
IO.println("Hello, Java 25");
}
Java 21で同じファイルを実行すると unnamed classes are a preview feature and are disabled by default. で止まります。21ではこの機能が「無名クラス」という名前のプレビューで、25までに名前と仕様が変わりました。JEP 512は java.lang.IO クラスを追加し、IO.println のようにクラス名を付けて呼ぶ形にしています。
JDK 25のjavacに --release 21 を付けると、機能ごとにどの版から使えるかがエラー文に出ます。モジュールimport・無名変数・柔軟なコンストラクタを含むファイルでは、次のように表示されました。
error: module imports are not supported in -source 21
(use -source 25 or higher to enable module imports)
error: unnamed variables are not supported in -source 21
(use -source 22 or higher to enable unnamed variables)
error: flexible constructors is not supported in -source 21
(use -source 25 or higher to enable flexible constructors)
柔軟なコンストラクタ本体を使うと、super(...) の前で引数を検証できます。親クラスの初期化前に不正値を弾けるので、検証用の static メソッドを別に書く必要がなくなります。import module java.base; は java.util や java.util.stream などをまとめて読み込む宣言で、モジュールの仕組みはJavaモジュールシステムの解説で扱っています。
API:Stream GatherersとFFM API
Stream Gatherersは、map や filter では書けない中間操作を足す仕組みです。List.of(1,2,3,4,5).stream().gather(Gatherers.windowFixed(2)).toList() は25で [[1, 2], [3, 4], [5]] を返し、--release 21 では cannot find symbol になります。FFM API(Foreign Function & Memory API)は22で正式化したネイティブ連携のAPIで、JNIのCグルーコードを書かずにCライブラリを呼べます。
25でもプレビューのままの機能
Java 21でプレビューだった機能が、すべて25で正式化したわけではありません。構造化並行性(JEP 505)は25で第5プレビュー、プリミティブ型パターン(JEP 507)は第3プレビュー、Vector API(JEP 508)は第10インキュベーターです。JDK 27でも構造化並行性は第7プレビューのままです。21でプレビューだったString Templatesは23で取り下げられ、25には存在しません。21で --enable-preview を付けて STR."..." を使っていたコードは書き直しが必要です。
仮想スレッドのpinning:21と25の実測差
Java 21の仮想スレッドは、synchronized ブロックの中でブロックすると、その間キャリアスレッドを手放しません。これがpinningで、24のJEP 491が解消しました。次は計測コードの主要部分です。実行するには java.util.concurrent.Executors をimportし、クラスの public static void main(String[] args) 内に置きます。21でコンパイルした同じクラスファイルを21と25で動かしました。
try (var ex = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 1000; i++) {
Object lock = new Object();
ex.submit(() -> {
synchronized (lock) {
try { Thread.sleep(100); } catch (InterruptedException e) { }
}
});
}
}
| JDK | 1回目 | 2回目 | 3回目 |
|---|---|---|---|
| Temurin 21.0.12.1 | 13,931ms | 14,526ms | 15,148ms |
| Temurin 25.0.4.1 | 290ms | 357ms | 244ms |
21では、16個のキャリアスレッドが sleep の間ずっと占有されるので、1,000本が順番待ちになります。25ではスリープ中にキャリアが解放され、ほぼ並列に終わります。synchronized を ReentrantLock へ置き換えるのは21で必要だった回避策で、25では不要です。ただしJEP 491は、ネイティブコードからJavaへ戻ってブロックする場合はpinningが残ると書いています。
21でpinningを探すのに使っていた -Djdk.tracePinnedThreads=short は、21では reason:MONITOR とスタックを出しましたが、25では何も出力しません。エラーにもならないため、監視の設定が効かなくなったことに気づきにくい変更です。25でpinningを調べるときはJFRの jdk.VirtualThreadPinned イベントを使います。仮想スレッドの仕組みはJava Virtual Threadの解説にまとめています。
メモリと起動時間:Compact Object HeadersとAOTキャッシュ
25で正式化したCompact Object Headers(JEP 519)は、64ビット環境のオブジェクトヘッダを縮めます。25では既定で無効で、-XX:+UseCompactObjectHeaders を付けたときだけ有効です。21でこのオプションを付けると Unrecognized VM option でJVMが起動しません。オブジェクトを100万個ずつ作り、jcmd <pid> GC.class_histogram でクラス別の合計バイト数を比べました。java.lang.Objectの行には、サンプルが生成したもの以外の同クラスのインスタンスも含まれます。
| オブジェクト(100万個) | 21・25既定 | 25+圧縮ヘッダ |
|---|---|---|
| record Point(int x, int y) | 24,000,000バイト | 16,000,000バイト |
| java.lang.Object | 16,001,280バイト | 8,000,640バイト |
小さなオブジェクトほど効果が大きく、intを2つ持つrecordは1個24バイトから16バイトになりました。JEP 519は、SPECjbb2015でヒープ使用量が22%減ったという計測を載せています。Java 27ではJEP 534で既定化されたので、25のうちに有効にして検証しておくと27以降への移行で慌てずに済みます。
起動時間は、JEP 514のAOTキャッシュで短くできます。25ではオプション1つで学習実行とキャッシュ作成が終わります。
java -XX:AOTCacheOutput=app.aot -jar app.jar # 学習実行とキャッシュ作成
java -XX:AOTCache=app.aot -jar app.jar # 2回目以降の起動
HttpClient・XMLパーサー・正規表現・Streamを使う小さなjarでは、22,925,312バイトのキャッシュができ、9回起動した中央値が1,504msから863msに縮みました。キャッシュは作成時と同じJDKビルドと互換性のあるクラスパスで使います。コンテナ運用では、イメージのビルド工程で生成する方法があります。
Java 21と25のサポート期限
サポート期限は「どのベンダーのビルドを使うか」で変わります。OpenJDKのライセンスと商用利用の範囲は別記事で扱い、ここでは期限だけを比べます。
Oracle JDKのPremier・Extended Support
Oracle Java SE Support Roadmapの期限は次のとおりです。
| 版 | Premier Support | Extended Support | 無償(NFTC)での更新 |
|---|---|---|---|
| Java 21 | 2028年9月 | 2031年9月 | 2026年9月まで |
| Java 25 | 2030年9月 | 2033年9月 | 2028年9月まで |
2026年10月時点で影響が大きいのは右端の列です。Oracleは同じロードマップのページで、2026年10月のCPU(Critical Patch Update)以降のOracle JDK 21の更新を、Java SE OTNライセンスで提供すると説明しています。OTNライセンスは個人・開発用途のほか、条件を満たすOracle承認製品用途やOCI用途も許諾しています。これらに該当しない本番商用利用では、別途有効な商用ライセンスが必要です。CPUとその間に出る更新の違いはOracle CSPUの解説を参照してください。
OpenJDKディストリビューションのサポート終了日
| ディストリビューション | Java 21 | Java 25 |
|---|---|---|
| Eclipse Temurin | 少なくとも2029年12月 | 少なくとも2031年9月 |
| Amazon Corretto | 2030年10月 | 2032年10月 |
| Microsoft Build of OpenJDK | 2028年9月 | 2030年9月 |
出典はAdoptiumのサポート表、Amazon Correttoのよくある質問、Microsoft Build of OpenJDKのサポート方針です。Correttoは最終の定期更新がEOLの3か月前(21は2030年7月、25は2032年7月)なので、計画はその月を基準に立てます。一般的な本番商用用途でJava 21の無償更新を続けるなら、ここに挙げたOpenJDKビルドが選択肢になります。Oracle JDK 21も、OTNの許諾用途では引き続き無償で利用できます。
Java 21から25へ上げると止まる箇所
アプリをコンパイルし直さなくても、JDKを入れ替えるだけで挙動が変わる箇所があります。同じクラスファイルを21と25で実行し、出力を比べました。
Security Managerの恒久無効化と32ビットx86ポートの削除
Security ManagerはJEP 486で恒久無効になりました。21では -Djava.security.manager=allow を付ければ System.setSecurityManager が警告付きで動きましたが、25では同じ指定でJVMの初期化が失敗します。
Error occurred during initialization of VM
java.lang.Error: A command line option has attempted to allow or enable the Security Manager. Enabling a Security Manager is not supported.
指定を外しても、コードから System.setSecurityManager を呼ぶと UnsupportedOperationException: Setting a Security Manager is not supported が出ます。起動スクリプトや環境変数 JAVA_TOOL_OPTIONS にこの指定が残っていないかを、移行の最初に検索してください。このほか、32ビットx86のポートは25で削除されたので、32ビットのx86環境ではJava 25を動かせません。
Unsafe・JNI・旧ZGCオプションの警告
sun.misc.Unsafe.allocateMemoryなどのメモリアクセスメソッドは、25でWARNING: A terminally deprecated method in sun.misc.Unsafe has been calledから始まる4行を標準エラーへ出します。--sun-misc-unsafe-memory-access=allowで抑止できますが、警告の発生元は古いライブラリであることが多く、ライブラリの更新が本来の対応です。System.loadLibraryなどJNIの呼び出しは、25でWARNING: A restricted method in java.lang.System has been calledを出します。クラスパス上のコードには--enable-native-access=ALL-UNNAMED、名前付きモジュールには--enable-native-access=対象モジュール名を指定すると、その対象の警告を解消できます。将来の版ではこの許可が無いと呼び出し自体が拒否される予定です。-XX:+UseZGC -XX:-ZGenerationalは25でIgnoring option ZGenerational; support was removed in 24.0を出して世代別ZGCで起動します。非世代モードは消えているので、ZGCのチューニングは測り直しが必要です(ZGCの仕組みとG1GCとの違い)。
ログ監視が標準エラーの「WARNING」を検知して通知する設定だと、JDKの入れ替えだけでアラートが増えます。ステージングで1度起動し、出た警告を起動オプションで抑えるか、ライブラリ更新で消すかを決めてから本番へ出します。
ビルドツールとライブラリの対応版
25でビルドすると、ツール側がクラスファイル版69を読めずに失敗することがあります。Java 25に対応した版は次のとおりです。
| ツール | Java 25対応 |
|---|---|
| Gradle | 9.1.0(2025年9月18日) |
| maven-compiler-plugin | 3.14.1以降(2025年9月22日) |
| Lombok | 1.18.40(2025年9月4日) |
| Byte Buddy | 1.17.0 |
| Spring Boot | 3.5系の現行版、4.0以降 |
Gradle 9.1.0のリリースノートはJava 25対応を明記しています。maven-compiler-pluginは3.14.0で Unsupported class file major version 69 が出る報告(issue #961)があり、3.14.1でASMの版を固定する修正が入りました。Byte BuddyはMockitoの内部で使われているため、テストだけが落ちるときはMockitoが取り込んでいるByte Buddyの版を確認します。Spring Boot 3.5.16のシステム要件は「Java 25まで互換」、4.1.1は「Java 26まで互換」です。Spring Boot 4への移行はSpring Boot 4の変更点で扱っています。
Java 21に留まるか25へ上げるかの判断基準
新規開発は25で始めます。21を選ぶ理由は、サポート期限の面ではもうありません。既存システムは、次の条件で分けます。
- 今月中に判断する:Oracle JDK 21を本番で無償利用している場合です。2026年10月のCPU以降の更新版を、OTNの許諾用途や既存契約でカバーされない本番商用用途で使うには、別途商用ライセンスが必要になります。依存ライブラリや運用ツールの25対応を確認できれば25へ移行します。確認が間に合わない場合は、Java 21のOpenJDKビルドへ切り替えて更新を継続する方法を選びます。
- すぐ25へ上げる:仮想スレッドを使っていて、
synchronizedを含むライブラリ(JDBCドライバ等)で詰まっている場合です。pinningの解消はJDKの入れ替えだけで効きます。 - 21に留める:Security Managerに依存するアプリケーションサーバーやプラグイン機構を使っている場合です。代わりの隔離方式(プロセスやコンテナの分離)へ設計を変えるまで、25では起動できません。
- 21に留める:利用中のフレームワークやAPMエージェントが25の対応版を出していない場合です。Temurinは少なくとも2029年12月まで、Correttoは2030年7月まで定期更新が続くので、対応版を待つ時間はあります。
バージョンアップを社内の手で回しきれない場合は、依存ライブラリの棚卸しから切り替えまでをシステムマイグレーション・リプレイスの支援として請け負っています。Java 8や11からまとめて上げる場合の進め方はJava近代化の進め方を参照してください。
Java 21と25に関するよくある質問
Java 21と25はどちらを使うべきですか?
新規開発なら25です。Oracle JDK 21は2026年10月のCPUから更新版のライセンスがOTNへ切り替わるため、用途ごとの利用条件を確認する必要があります。21と25を比較すると、25では新しい言語機能とpinningの改善を利用できます。既存システムでは、Security Managerへの依存が残る場合や、利用中のフレームワーク・APMエージェントが25に未対応の場合は、対応を終えるまで21に留めます。
Java 25はLTSですか?
LTSです。Oracleは2030年9月までPremier Support、2033年9月までExtended Supportを提供します。Temurinは少なくとも2031年9月まで、Correttoは2032年10月のEOLまでサポートします。
Java 21のサポート期限(EOL)はいつまでですか?
Oracle JDKはPremier Supportが2028年9月、Extended Supportが2031年9月までです。ただし無償ライセンス(NFTC)での更新は2026年10月のCPUの前までで、それ以降の更新はJava SE OTNライセンスになります。OpenJDKビルドではTemurinが少なくとも2029年12月まで、Correttoが2030年10月、Microsoft Build of OpenJDKが2028年9月までです。
Javaの最新バージョンはいくつですか?
2026年10月時点の最新はJava 27(2026年9月15日GA)で、非LTSです。最新のLTSはJava 25で、次のLTSは2027年9月予定のJava 29です。
Java 21でコンパイルしたクラスはJava 25で動きますか?
通常のクラスファイルは原則として動きます。今回の計測でも21でコンパイルしたクラスを25で実行しました。ただし、21のプレビュー機能に依存するクラスファイルは、そのまま25では実行できず、ソースの見直しと再コンパイルが必要です。逆に25の既定で出力したクラスは21では動かないため、21でも動かす必要があるなら --release 21 を付けてビルドします。Security Managerの有効化・Unsafe・JNIを使うコードは、上の「止まる箇所」の挙動差を確認してください。