Java 24(JDK 24)は2025年3月18日にGAとなった非LTSのフィーチャーリリースです。「java 24 lts」で検索されることがありますが、24はLTSではありません。OracleのPremier Supportは2025年9月に終わり、アップデートは2025年7月15日の24.0.2が最後でした。本記事では、JDK 24に入った24件のJEPがJava 25・26・27でどうなったかを一覧にし、Stream Gatherersや仮想スレッドのピン留め解消、AOTキャッシュをJDK 21〜27で実際に動かした結果をもとに、24から移る先の選び方まで整理します。最新版のJava 27はJava 27の非LTS運用とG1・オブジェクトヘッダ既定化の影響で扱っています。
まとめ:Java 24の要点
- Java 24はLTSではありません。GAは2025年3月18日、OracleのPremier Supportは2025年9月で終了し、Extended Supportはありません。最終アップデートは24.0.2(2025年7月15日)です。
- 24件のJEPのうち14件は正式機能として入り、10件はプレビュー・インキュベーター・実験的機能でした。プレビューだったスコープ付き値・モジュールインポート宣言・柔軟なコンストラクタ本体・シンプルなソースファイルは、Java 25で正式になっています。
- 構造化並行性とプリミティブ型のパターンは、Java 27でもまだプレビューです。構造化並行性はJDK 28(2027年3月予定)に正式機能として統合済みです。
- synchronizedの中で待っても仮想スレッドがキャリアを占有しなくなりました(JEP 491)。実測では同じコードがJDK 21で14,228ミリ秒、JDK 24で372ミリ秒でした。
- 移行で止まりやすいのはSecurity Managerの無効化とZGCの非世代モード削除です。
-Djava.security.manager=allowはJDK 24で起動エラーになり、-XX:-ZGenerationalは26以降で起動できなくなります。 - 今24を使っているなら、移行先はJava 25 LTSが第一候補です。24でコンパイルしたプレビュー機能入りのクラスは25では動かないため、再コンパイルが必要です。
Java 24のサポート期限と最終アップデート
非LTSのJDKは、次の版が出る半年後に更新が止まります。OracleのJava SE Support Roadmapでは、22〜24の非LTSはPremier Supportがそれぞれ次の9月または3月までで、Extended Supportは「Not Available」です。24の場合は2025年9月が期限でした。
アップデートは2回だけです。JDK 24のリリースノートに載っているのは24.0.1(2025年4月15日)と24.0.2(2025年7月15日)で、Adoptium(Eclipse Temurin)のリリース一覧も jdk-24.0.2+12 が最後です。2025年10月以降に見つかった脆弱性の修正は、24には提供されていません。
| 版 | 区分 | GA | Oracle Premier Support | Extended Support |
|---|---|---|---|---|
| Java 21 | LTS | 2023年9月 | 2028年9月 | 2031年9月 |
| Java 24 | 非LTS | 2025年3月18日 | 2025年9月 | なし |
| Java 25 | LTS | 2025年9月16日 | 2030年9月 | 2033年9月 |
| Java 26 | 非LTS | 2026年3月17日 | 2026年9月 | なし |
| Java 27 | 非LTS | 2026年9月15日 | 2027年3月 | なし |
24のクラスファイルのメジャーバージョンは68です(javap -v で major version: 68 を確認)。古いバイナリがどの版でビルドされたかを調べるときの目印になります。OracleとOpenJDKビルドの無償範囲の違いはOpenJDKの商用利用とサポート期限にまとめています。
JDK 24の24件のJEPと後継版での扱い
OpenJDKのJDK 24プロジェクトページに載るJEPは24件で、そのうち10件はプレビュー・インキュベーター・実験的機能でした。JEP(JDK Enhancement Proposal)は、JDKへの機能追加や変更を提案・管理する文書です。番号は提案を識別するもので、すべての提案が正式機能になるとは限りません。下表の「その後」は、JDK 25・26・27・28のプロジェクトページと各JEPのステータスで確認した2026年10月時点の状態です。
| JEP | 内容 | 24での段階 | その後 |
|---|---|---|---|
| 404 | 世代別Shenandoah | 実験的 | 25で製品機能(521)、28で既定化予定(535) |
| 450 | コンパクトオブジェクトヘッダ | 実験的 | 25で製品機能(519)、27で既定(534) |
| 472 | JNI利用制限の準備 | 正式 | 警告のまま |
| 475 | G1のバリア展開の後段化 | 正式 | - |
| 478 | 鍵導出関数(KDF)API | プレビュー | 25で正式(510) |
| 479 | Windows 32bit x86ポート削除 | 正式 | - |
| 483 | AOTクラスロード&リンク | 正式 | 25で作成手順を簡略化(514)、メソッドプロファイルを保存(515) |
| 484 | Class-File API | 正式 | - |
| 485 | Stream Gatherers | 正式 | - |
| 486 | Security Managerの恒久無効化 | 正式 | API本体は将来削除 |
| 487 | スコープ付き値 | 第4プレビュー | 25で正式(506) |
| 488 | プリミティブ型のパターン | 第2プレビュー | 27でも第5プレビュー(532) |
| 489 | Vector API | 第9インキュベーター | 27で第12インキュベーター(537) |
| 490 | ZGCの非世代モード削除 | 正式 | 26でフラグ自体が起動エラー |
| 491 | synchronizedでピン留めしない仮想スレッド | 正式 | - |
| 492 | 柔軟なコンストラクタ本体 | 第3プレビュー | 25で正式(513) |
| 493 | JMODなしのjlink | 正式(ビルド時に選択) | - |
| 494 | モジュールインポート宣言 | 第2プレビュー | 25で正式(511) |
| 495 | シンプルなソースファイルとインスタンスmain | 第4プレビュー | 25で正式(512) |
| 496 | ML-KEM(耐量子の鍵カプセル化) | 正式 | 27でTLS 1.3のハイブリッド鍵交換(527) |
| 497 | ML-DSA(耐量子の署名) | 正式 | - |
| 498 | Unsafeのメモリアクセスで警告 | 正式 | 27でも警告のまま(実測) |
| 499 | 構造化並行性 | 第4プレビュー | 27で第7プレビュー(533)、28で正式化予定(543) |
| 501 | 32bit x86ポートの非推奨化 | 正式 | 25で削除(503) |
表の括弧内は後継のJEP番号です。JDK 28は2027年3月GA予定で、構造化並行性の正式版(JEP 543)は2026年10月時点でStatusが「Integrated」です。
Java 24の正式機能と実行結果
ここからのコードは、Eclipse Temurin 24.0.2をIntel Core i9-9880H(8コア16スレッド)のmacOSで動かした結果です。プレビュー用のフラグは付けていません。
Stream Gatherers(JEP 485)の中間操作と出力例
Stream.gather は、map や filter では書けなかった「固定長でまとめる」「累積しながら流す」といった中間操作を差し込むAPIです。標準で用意された Gatherers の5つを試しました。
List<Integer> sales = List.of(120, 80, 150, 90, 200, 60, 170);
sales.stream().gather(Gatherers.windowFixed(3)).toList();
// [[120, 80, 150], [90, 200, 60], [170]]
sales.stream().gather(Gatherers.windowSliding(3))
.map(w -> w.stream().mapToInt(Integer::intValue).sum() / w.size()).toList();
// [116, 106, 146, 116, 143] 3件移動平均
sales.stream().gather(Gatherers.scan(() -> 0, Integer::sum)).toList();
// [120, 200, 350, 440, 640, 700, 870] 累計
sales.stream().gather(Gatherers.fold(() -> 0, Math::max)).toList();
// [200]
Stream.of("a", "b", "c", "d", "e", "f", "g", "h")
.gather(Gatherers.mapConcurrent(4, s -> {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
return s.toUpperCase();
}))
.toList();
// [A, B, C, D, E, F, G, H] 753 ms
mapConcurrent は仮想スレッドで最大4件を同時に処理し、結果の順序は入力どおりに保たれます。200ミリ秒かかる処理8件が753ミリ秒で終わり、逐次実行なら1,600ミリ秒かかる計算です。並列ストリームと違い同時実行数を自分で決められるので、外部APIへの同時接続数を絞りたい場面に向きます。ラムダとStream APIの使い分けはJavaラムダ式とStream APIの使いどころで扱っています。
synchronizedでの仮想スレッドのピン留め解消(JEP 491)
Java 21の仮想スレッドは、synchronized ブロックの中でブロックするとキャリアスレッド(OSスレッド)を手放せず、他の仮想スレッドが待たされました。JEP 491でこの制約が外れています。1,000本の仮想スレッドがそれぞれ別のロックを取り、中で100ミリ秒スリープするコードを各版で実行しました。
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) {
Thread.currentThread().interrupt();
}
}
});
}
}
| JDK | 完了までの時間 |
|---|---|
| 21.0.13(LTS) | 14,228 ms |
| 24.0.2 | 372 ms |
| 25.0.4.1(LTS) | 366 ms |
| 27 | 342 ms |
21ではキャリアが16本しかないため、スリープ中の仮想スレッドがキャリアを占有して順番待ちになります。24以降は全スレッドがほぼ同時に眠れます。synchronized を ReentrantLock に書き換える作業は、ピン留め回避だけが目的なら24以降では不要です。
ただし、ピン留めがすべて消えたわけではありません。JEP 491は、ネイティブメソッドやFFM API経由のネイティブコードからJavaへ戻ってブロックする場合や、クラス初期化子の中でブロックする場合は引き続きピン留めされると明記しています。別のスレッドによるクラス初期化の完了を待つケースは、JDK 26(JDK-8369238)で多くの場合キャリアを手放すよう改善されました。残ったケースはJFRの jdk.VirtualThreadPinned イベントで検出でき、21で使えた jdk.tracePinnedThreads システムプロパティは不要になりました。仮想スレッドの基本はJava仮想スレッドとProject Loom入門を参照してください。
耐量子暗号ML-KEM・ML-DSA(JEP 496・497)
NISTが2024年に標準化したFIPS 203(ML-KEM)とFIPS 204(ML-DSA)が、追加ライブラリなしで使えるようになりました。鍵カプセル化は javax.crypto.KEM、署名は既存の Signature で扱います。
KeyPair kp = KeyPairGenerator.getInstance("ML-KEM").generateKeyPair();
KEM kem = KEM.getInstance("ML-KEM");
KEM.Encapsulated enc = kem.newEncapsulator(kp.getPublic()).encapsulate();
SecretKey k2 = kem.newDecapsulator(kp.getPrivate()).decapsulate(enc.encapsulation());
// 送信側の enc.key() と受信側の k2 が同じ32バイトの共有鍵になる
| パラメータセット | 実測サイズ |
|---|---|
| ML-KEM-512 | 公開鍵 822バイト |
| ML-KEM-768(既定) | 公開鍵 1,206バイト・暗号文 1,088バイト |
| ML-KEM-1024 | 公開鍵 1,590バイト |
| ML-DSA-44 | 署名 2,420バイト |
| ML-DSA-65(既定) | 署名 3,309バイト |
| ML-DSA-87 | 署名 4,627バイト |
公開鍵はX.509エンコード後の長さです。getInstance("ML-KEM") のように名前だけを渡すと、ML-KEM-768とML-DSA-65が選ばれました。ECDSAの署名が数十バイトなのに比べ、ML-DSAは数キロバイトになります。証明書やトークンに載せるならサイズの見積もりが要ります。Java 27では、このML-KEMを使うTLS 1.3のハイブリッド鍵交換(X25519MLKEM768 など)がJEP 527で入りました。耐量子暗号の全体像は耐量子暗号とML-KEMの仕組みにあります。
AOTクラスロード&リンク(JEP 483)の起動時間と制約
1回目の実行(トレーニング)で読み込んだクラスを記録し、ロードとリンクを済ませた状態をキャッシュファイルに保存します。JDK 24では2段階で作ります。
java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconf -cp app.jar AppStart
java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconf -XX:AOTCache=app.aot -cp app.jar
java -XX:AOTCache=app.aot -cp app.jar AppStart
Stream・java.time・ロギング・XMLパーサーを少しずつ使う小さなアプリで、10回ずつ起動した中央値はキャッシュなし719ミリ秒、キャッシュあり348ミリ秒でした。キャッシュファイルは約14MBです。測定では各条件で12回起動し、最初の2回を除いた10回の中央値を使いました。条件が合わないとキャッシュは作れないか、使われません。
- クラスパスにディレクトリを含めると作れない。
-cp classesのままでは作成段階がError occurred during CDS dumpingとCannot have non-empty directory in pathsで失敗します。JEP 483も「Class paths must contain only JAR files」と定めています。 - クラスパスが変わると警告を出してキャッシュなしで起動する。同じ中身のJARを別名にすると
shared class paths mismatchの警告だけ出て、キャッシュなしで起動します。-XX:AOTMode=onを付ければ、無効なときに起動を止められます。 - JDKの版をまたげない。24で作ったキャッシュを25で読むと
The AOT cache version 0x12 does not match the required version 0x13.になります。JDKを上げたらトレーニングからやり直します。
Java 25では -XX:AOTCacheOutput=app.aot の1回でキャッシュを作れるようになりました(JEP 514)。24でこのオプションを渡すと Unrecognized VM option で起動しません。
Class-File APIと@since 24のAPI(JEP 484)
java.lang.classfile パッケージで、ASMなどの外部ライブラリなしに .class ファイルを読み書きできます。先ほどのクラスを読ませると、メジャーバージョンとメソッド一覧が取れました。
ClassModel cm = ClassFile.of().parse(Files.readAllBytes(Path.of(args[0])));
System.out.println(cm.thisClass().asInternalName() + " major=" + cm.majorVersion());
for (MethodModel m : cm.methods()) {
System.out.println(" " + m.methodName().stringValue() + m.methodType().stringValue());
}
// GatherersDemo major=68
// main([Ljava/lang/String;)V ...
JDK 24のAPI仕様のNew API一覧で「24」と付くもののうち、Class-File APIとStream Gatherers以外では Reader.of(CharSequence)、Process.waitFor(Duration)、仮想スレッドのスケジューラを監視する jdk.management.VirtualThreadSchedulerMXBean、ML-KEM・ML-DSAのパラメータを指す NamedParameterSpec.ML_KEM_768 などの定数が実務で使えます。このうちReader.of、Process.waitFor(Duration)、VirtualThreadSchedulerMXBeanを使うコードをJDK 21でコンパイルすると、いずれも cannot find symbol などのエラーになりました。
Java 24で起動しなくなる・警告が出る変更
Security Managerの恒久無効化(JEP 486)
17で非推奨になったSecurity Managerは、24で有効にできなくなりました。-Djava.security.manager=allow を付けると、起動の時点で止まります。
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 が投げられます。System.getSecurityManager() は常に null です。起動スクリプトに java.security.manager の指定が残っていないか、移行前に確認してください。
ZGCの非世代モード削除(JEP 490)
24から、ZGCは世代別モードだけになりました。-XX:+UseZGC -XX:-ZGenerational を各版で試すと、扱いが2段階で変わります。
- 24と25:
Ignoring option ZGenerational; support was removed in 24.0の警告を出し、世代別ZGCで起動する - 26と27:
Unrecognized VM option 'ZGenerational'で起動に失敗する
24や25では警告だけで動くため見落とされがちですが、26以降へ上げた時点で起動しなくなります。ZGC全般はZGCの仕組みとG1GCとの違いで解説しています。
sun.misc.Unsafeのメモリアクセス警告(JEP 498)
Unsafe.allocateMemory などを初めて呼んだ時点で、4行の警告が標準エラーに出ます。
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::allocateMemory has been called by UnsafeDemo (file:...)
WARNING: Please consider reporting this to the maintainers of class UnsafeDemo
WARNING: sun.misc.Unsafe::allocateMemory will be removed in a future release
呼び出し元のクラスとJARが表示されるので、どのライブラリが使っているかはここで特定できます。--sun-misc-unsafe-memory-access=deny を付けると UnsupportedOperationException: allocateMemory で止まるため、将来の削除に備えた検証に使えます。JEP 498は「JDK 26以降で例外を投げる」予定と書いていますが、26.0.2.1とJava 27でも既定は警告のままでした。
このほか、JNIでネイティブライブラリを読み込むと警告が出るようになりました(JEP 472)。クラスパス上のコードには --enable-native-access=ALL-UNNAMED を指定します。名前付きモジュールには、--enable-native-access=対象モジュール名 で個別にネイティブアクセスを許可します。
Java 24のプレビュー機能の正式化状況と再コンパイル
JDK 24でプレビューだった5つの言語機能・APIを、各版で --enable-preview を付けずにコンパイルしました。
| 機能 | 24 | 25 | 26 | 27 |
|---|---|---|---|---|
| スコープ付き値 | エラー | 通る | 通る | 通る |
| モジュールインポート宣言 | エラー | 通る | 通る | 通る |
| 柔軟なコンストラクタ本体 | エラー | 通る | 通る | 通る |
| クラス宣言なしの void main() | エラー | 通る | 通る | 通る |
| プリミティブ型のパターン | エラー | エラー | エラー | エラー |
24でのエラーは error: module imports are a preview feature and are disabled by default. の形で、機能ごとに名前が変わります。柔軟なコンストラクタ本体は、super(...) の前に引数を検査できる機能です。
class Positive {
Positive(int v) {
if (v < 1) throw new IllegalArgumentException("positive value required");
}
}
class Port extends Positive {
Port(int v) {
if (v < 1 || v > 65535) throw new IllegalArgumentException("port out of range: " + v);
super(v);
}
}
注意したいのは、24で --enable-preview を付けてコンパイルしたクラスです。25で --enable-preview を付けて実行しても、次のエラーで読み込めません。
java.lang.UnsupportedClassVersionError: FlexCtor (class file version 68.65535) was compiled with preview features that are unsupported. This version of the Java Runtime only recognizes preview features for class file version 69.65535
プレビュー機能を使ったクラスは、実行するJDKと同じ版でしか動きません。24でプレビュー機能を試していたコードは、25に上げるときにソースから再コンパイルが必要です。
実験的機能も同じ傾向です。コンパクトオブジェクトヘッダ(-XX:+UseCompactObjectHeaders)と世代別Shenandoahは、24では -XX:+UnlockExperimentalVMOptions がないと起動エラーになり、25ではそのまま起動しました。構造化並行性の25〜27でのAPI変更はJava 26の新機能10個のJEPと現在地で追っています。
Java 24からの移行先の選び方
24で動いているシステムは、Java 25 LTSへ移すのが基本です。24で正式になった機能は25でもそのまま使え、25のPremier Supportは2030年9月まであります。24から上げるときに確認するのは、上で挙げたプレビュー機能の再コンパイルと、ライブラリやビルドツールが25に対応しているかです。21と25の差分はJava 21と25の違いに実測付きでまとめています。
Java 26は2026年9月にPremier Supportが終わっており、移行先には向きません。27はJEP 534でコンパクトオブジェクトヘッダが既定になり、メモリが減る効果を得られますが、2027年3月に28が出れば更新が止まります。半年ごとにJDKを上げるCIとテストの体制がないなら、27は選ばない方が安全です。
21以前から一気に上げる場合は、24で入ったSecurity Managerの無効化とUnsafe警告が21と25の間に挟まります。8や11から段階的に上げる手順はJava近代化の進め方で扱っています。基幹システムのJava移行を外部に任せる場合は、issohのシステムマイグレーション・リプレイスでも相談を受けています。
よくある質問
Java 24はLTS(長期サポート)ですか?
いいえ。Java 24は非LTSで、OracleのPremier Supportは2025年9月に終わり、Extended Supportはありません。前後のLTSはJava 21とJava 25です。
Java 24のサポート期限と最終バージョンは?
サポート期限は2025年9月で、最終アップデートは2025年7月15日公開の24.0.2です。Eclipse Temurinも jdk-24.0.2+12 が最後のビルドで、以降のセキュリティ修正はありません。
Java 24(JDK 24)は今でもダウンロードできますか?
できます。OracleのJava SE 24 Archive Downloadsとjdk.java.netのアーカイブに24.0.2まで残っています。セキュリティ修正が止まった版なので、古いアプリの再現確認などの検証用途に限ってください。
Java 24とJava 25の違いは何ですか?
最大の違いはサポート区分で、25はLTSです。機能面では、24でプレビューだったスコープ付き値・モジュールインポート宣言・柔軟なコンストラクタ本体・シンプルなソースファイルが25で正式になり、AOTキャッシュも1回のコマンドで作れるようになりました。
JEPとは何ですか?
JEP(JDK Enhancement Proposal)は、JDKへの機能追加や変更を提案・管理する文書です。提案ごとに番号が振られます。プレビューやインキュベーターを経る提案もありますが、必須の段階ではなく、正式化せず撤回される場合もあります。JDK 24に24件のJEPが入ったのは偶然で、版番号とは関係ありません。