Java 24新機能まとめ

Java 24が2025/3/18にリリースされました。
[Java 24 / JDK 24: General Availability](https://mail.openjdk.org/pipermail/jdk-dev/2025-March/009843.html)
[The Arrival of Java 24](https://blogs.oracle.com/java/post/the-arrival-of-java-24)
[オラクル、Java 24をリリース | Oracle 日本](https://www.oracle.com/jp/news/announcement/oracle-releases-java-24-2025-03-18/)
<!--
[Java 23をリリース](https://blogs.oracle.com/oracle4engineer/post/the-arrival-of-java-23-ja)
-->
LTSではないですが多くのJEPが入っています。Preview機能も次のLTSであるJava 25では標準化しそうなものが多く、予習のために触っておくのがいいと思います。

<!--
こちらの配信で解説しています。
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228058/1601b660-d2f8-36f7-1cba-f852930261e1.png)](https://www.youtube.com/watch?v=-5zlmBnxmCU)
-->

JDKをインストールせずに言語やライブラリの新機能を試したい場合にはJava Playgroundが便利です。
https://dev.java/playground/

Samplesに新機能のサンプルがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228058/3943c0bd-ea43-4d57-8e5d-8b815ba0d31f.png)

## イベント

新機能解説で東京と福岡で登壇します。

東京では3/26(水)に品川のマイクロソフトのオフィスで19時から行います。(終了しました)
[【オフライン】JJUGナイトセミナー「Java 24 リリース記念イベント」3/26(水) 開催 - 日本Javaユーザーグループ/Japan Java User Group | Doorkeeper](https://jjug.doorkeeper.jp/events/182744)

福岡では4/11(金)にLINEヤフー博多深見オフィスで19時から行います。
[Java 24リリースイベント@福岡 - connpass](https://javaq.connpass.com/event/348733/)

## 資料

詳細はこちら
[JDK 24 Release Notes](https://jdk.java.net/24/release-notes)
[Java SE 24 Platform JSR 399](https://openjdk.org/projects/jdk/24/spec/)
[OpenJDK JDK 24 GA Release](https://jdk.java.net/24/)

APIドキュメントはこちら
[Overview (Java SE 24 & JDK 24)](https://docs.oracle.com/en/java/javase/24/docs/api/index.html)

追加されたAPIまとめはこちら
https://docs.oracle.com/en/java/javase/24/docs/api/new-list.html

APIの差分はこちら。
https://cr.openjdk.org/~iris/se/24/build/latest/java-se--jdk-23-ga--jdk-24%2B36/

<!--
Java 17以降で取り込まれたJEPはこちら
[JEPs in JDK 22 integrated since JDK 17](https://openjdk.org/projects/jdk/22/jeps-since-jdk-17)
-->

## ディストリビューション

MacやLinuxでのインストールには[SDKMAN!](https://sdkman.io/)をお勧めします。

[Oracle OpenJDK](https://jdk.java.net/24/)以外に無償で商用利用できるディストリビューションとしては、次のようなものがあります。
Microsoft Buildは非LTSはお休み。

- [Oracle JDK](https://www.oracle.com/java/technologies/downloads/)
- [Adoptium Temurin](https://adoptium.net/?variant=openjdk24&jvmVariant=hotspot)
- [Liberica JDK](https://bell-sw.com/pages/downloads/#jdk-24)
- [Amazon Corretto 24](https://docs.aws.amazon.com/corretto/latest/corretto-24-ug/downloads-list.html)
- [SapMachine](https://sap.github.io/SapMachine/)
- [Azul Zulu](https://www.azul.com/downloads/?version=java-24-lts&package=jdk#zulu)
<!--
- [Microsoft Build of OpenJDK](https://learn.microsoft.com/java/openjdk/download)
-->

どのディストリビューションを使えばいいかわからないときは、とりあえずAdoptium Temurinが無難です。
アップデートは4月に24.0.1が、7月に24.0.2がリリースされることになります。

# 開発組織

関わった開発組織は、こんな感じになってます。Java 23に比べると、AmazonとAlibabaが増えてるかな。あとArmがちょっと増えてるのはArm Windows対応だろうか。  
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228058/278bd028-57e2-464c-aadc-f578d8d57dc1.png)

# JEP
大きめの変更はJEPでまとまっています。
https://openjdk.org/projects/jdk/24/

今回は24個のJEPが取り込まれました。正式採用は14個です。プレビューからの正式化はClass-File APIとStream Gatherersの2つです。プレビューは10個で、そのうち3つが新たに入ったものです。

今回は次の6つのカテゴリにまとめてます
- [言語機能](#言語機能)
- [API](#api)
- [JVM](#jvm)
- [Security](#security)
- [ツール](#ツール)
- [JDK](#jdk)

[404: Generational Shenandoah (Experimental)](#404-generational-shenandoah-experimental)
[450: Compact Object Headers (Experimental)](#450-compact-object-headers-experimental)
[472: Prepare to Restrict the Use of JNI](#472-prepare-to-restrict-the-use-of-jni)
[475: Late Barrier Expansion for G1](#475-late-barrier-expansion-for-g1)
[478: Key Derivation Function API (Preview)](#478-key-derivation-function-api-preview)
[479: Remove the Windows 32-bit x86 Port](#479-remove-the-windows-32-bit-x86-port)
[483: Ahead-of-Time Class Loading & Linking](#483-ahead-of-time-class-loading--linking)
[484: Class-File API](#484-class-file-api)
[485: Stream Gatherers](#485-stream-gatherers)
[486: Permanently Disable the Security Manager](#486-permanently-disable-the-security-manager)
[487: Scoped Values (Fourth Preview)](#487-scoped-values-forth-preview)
[488: Primitive Types in Patterns, instanceof, and switch (Second Preview)](#488-primitive-types-in-patterns-instanceof-and-switch-second-preview)
[489: Vector API (Ninth Incubator)](#489-vector-api-ninth-incubator)
[490: ZGC: Remove the Non-Generational Mode](#490-zgc-remove-the-non-generational-mode)
[491: Synchronize Virtual Threads without Pinning](#491-synchronize-virtual-threads-without-pinning)
[492: Flexible Constructor Bodies (Third Preview)](#492-flexible-constructor-bodies-third-preview)
[493: Linking Run-Time Images without JMODs](#493-linking-run-time-images-without-jmods)
[494: Module Import Declarations (Second Preview)](#494-module-import-declarations-second-preview)
[495: Simple Source Files and Instance Main Methods (Fourth Preview)](#495-simple-source-files-and-instance-main-methods-fourth-preview)
[496: Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism](#496-quantum-resistant-module-lattice-based-key-encapsulation-mechanism)
[497: Quantum-Resistant Module-Lattice-Based Digital Signature Algorithm](#497-quantum-resistant-module-lattice-based-digital-signature-algorithm)
[498: Warn upon Use of Memory-Access Methods in sun.misc.Unsafe](#498-warn-upon-use-of-memory-access-methods-in-sunmiscunsafe)
[499: Structured Concurrency (Fourth Preview)](#499-structured-concurrency-fourth-preview)
[501: Deprecate the 32-bit x86 Port for Removal](#501-deprecate-the-32-bit-x86-port-for-removal)

24個というのは、半年リリースがまわりだしたJava 10以降では最大ですね。
![Untitled-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228058/b8e3a7d8-3932-49fc-922a-e47010b2a284.png)

# 言語機能

言語機能の変更としては、新しいものはなく正式化されたものもないですが、Java 25 LTSに向けて慎重に評価している状況のようです。Primitive Types in Patterns...以外は、Java 25で正式化しそう。

[488: Primitive Types in Patterns, instanceof, and switch (Second Preview)](#488-primitive-types-in-patterns-instanceof-and-switch-second-preview)
[492: Flexible Constructor Bodies (Third Preview)](#492-flexible-constructor-bodies-third-preview)
[494: Module Import Declarations (Second Preview)](#494-module-import-declarations-second-preview)
[495: Simple Source Files and Instance Main Methods (Fourth Preview)](#495-simple-source-files-and-instance-main-methods-fourth-preview)

## [488: Primitive Types in Patterns, instanceof, and switch (Second Preview)](https://openjdk.org/jeps/488)

Java 23でプレビューとして入りましたが、変更なく2ndプレビューになりました。[3rdプレビューのJEP](https://openjdk.org/jeps/8349215)が用意されているので、Java 25LTSでの正式化はなさそう。

### instanceof

instanceofでプリミティブを使うとき、値が欠落しないかどうかが判定基準になります。

```
jshell> int i = 128
i ==> 128

jshell> i instanceof byte
$2 ==> false

jshell> i = 127
i ==> 127

jshell> i instanceof byte
$4 ==> true
```

doubleでも精度落ちするかどうかが判定されます。

```
jshell> double d = 1
d ==> 1.0

jshell> d instanceof int
$2 ==> true

jshell> d = 1.2
d ==> 1.2

jshell> d instanceof int
$4 ==> false
```

ただし、ラッパークラスのオブジェクトの場合はその基本型のときだけtrueになります。

```
jshell> Long num = 123L
num ==> 123

jshell> num instanceof int
|  エラー:
|  不適合な型: java.lang.Longをintに変換できません:
|  num instanceof int
|  ^-^

jshell> num instanceof long
$7 ==> true
```

そしてパターンマッチとして使えるわけですね。

```
jshell> long num = 123
num ==> 123

jshell> var out = new ByteArrayOutputStream()
out ==>

jshell> if (num instanceof byte b) out.write(b)

jshell> out.toByteArray()
$21 ==> byte[1] { 123 }

jshell> num = 1234
num ==> 1234

jshell> if (num instanceof byte b) out.write(b)

jshell> out.toByteArray()
$24 ==> byte[1] { 123 }
```

### switch

このパターンマッチがswitchでも使えるということなんですけど、定数パターンもあるので、switchですべての型が使えるようになったということになります。

```
jshell> long num = 1234
num ==> 1234

jshell> switch (num) { case int n -> "いんと"; default -> "それ以外";}
$25 ==> "いんと"

jshell> num = 30_0000_0000L
num ==> 3000000000

jshell> switch (num) { case int n -> "いんと"; default -> "それ以外";}
$27 ==> "それ以外"
```

if式のようなこともできますね。
```
boolean flag = isFoo();
var s = switch (flag) {
  case true -> "たった";
  case false -> "おりた";
}
```

## [492: Flexible Constructor Bodies (Third Preview)](https://openjdk.org/jeps/492)

Java 22でStates before superだったものがJava 23で名前が変わり、大きな変更なく3rdプレビューになりました。[PreviewのとれたJEP](https://openjdk.org/jeps/8344702)も用意されているので、Java 25で正式化しそうです。

スーパークラスのコンストラクタ呼び出しの前に`this`を使わないステートメントが書けるようになります。

例えばListを2つコンストラクタにとるクラスがあるとします。

```java
class Foo {
  Foo(List l1, List l2) {
  }
}
```

このコンストラクタを継承するとき、Listをふたつ渡す必要があります。 
```java
class Bar extends Foo {
  Bar() {
    super(List.of("abc", "def"), List.of("abc", "def"));
  }
}
```

同じものを渡しているので共通化したいですが、`super`の前でステートメントを実行できないので迂回を考える必要があります。

```java
class Bar extends Foo {
  private Bar(List l) {
    super(l, l);
  }
  Bar() {
    this(List.of("abc", "def"));
  }
}
```

これを、次のように書けるようになります。

```java
class Bar extends Foo {
  Bar() {
    var param = List.of("abc", "def");
    super(param, param);
  }
```

このように、パラメータの検証や構築、共有がある場合に、自然なコードで書けるようになります。
次のようなフィールドのアクセスを`super`の前に行うとエラーになります。

```java
class Bar extends Foo {
  List<String> param;
  Bar() {
    param = List.of("abc", "def");
    super(param, param);
  }
}
```

```
test.java:11: エラー: スーパータイプのコンストラクタの呼出し前はparamを参照できません
    param = List.of("abc", "def");
    ^
```

## [494: Module Import Declarations (Second Preview)](https://openjdk.org/jeps/494)

Java 23でプレビューとして入り、2つの変更と共に2ndプレビューになりました。[PreviewのとれたJEP](https://openjdk.org/jeps/8344700)も用意されているので、Java 25で正式化されそう。

モジュールごとのimportができるようになりました。java.baseをimportしたらだいたいいけるってなりそうです。あと、外部ライブラリのモジュール対応が進みそうです。

Java 23のときからの変更で、依存のあるモジュールもimportされるようになりました。そのためjava.seをimportすると、Java SEのすべてのAPIがimportされます。

次のようにすると、java.baseモジュールに属するjava.utilもjava.ioもimportされることになります。

```java
import module java.base;
```

JShellでも`--enable-preview`をつけるとデフォルトでjava.baseがimportされるようになります。
例えば、java.timeはimportされていなかったのでLocalDateをそのまま使うとエラーになります。

```
>jshell
|  JShellへようこそ -- バージョン23
|  概要については、次を入力してください: /help intro

jshell> LocalDate.now()
|  エラー:
|  シンボルを見つけられません
|    シンボル:   変数 LocalDate
|    場所: クラス
|  LocalDate.now()
|  ^-------^
```
`--enable-preview`を付けるとimport不要で使えます。

```
>jshell --enable-preview
|  JShellへようこそ -- バージョン23
|  概要については、次を入力してください: /help intro

jshell> LocalDate.now()
$1 ==> 2024-10-11
```

SwingでGUIプログラムをする場合には`java.desktop`モジュールをimportします。

```
jshell> import module java.desktop;

jshell> var f = new JFrame("hello")
f ==> javax.swing.JFrame[frame0,0,0,0x0,invalid,hidden, ... tPaneCheckingEnabled=true]

jshell> var b = new JButton("OK")
b ==> javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0 ... xt=OK,defaultCapable=true]
```

ただ、そうすると`List`が`java.util.List`と`java.awt.List`でかぶるので、あいまいになってそのままで使えなくなります。

```
jshell> List a = null;
|  エラー:
|  Listの参照はあいまいです
|    java.awtのクラス java.awt.Listとjava.utilのインタフェース java.util.Listの両方が一致します
|  List a = null;
|  ^--^
```

この場合には`java.util.List`など実際に使うほうをimportするなどが必要です。

```
jshell> import java.util.List

jshell> List a = null
a ==> null
```

## [495: Simple Source Files and Instance Main Methods (Fourth Preview)](https://openjdk.org/jeps/495)

Java 23で3rdプレビューになっていたImplicitly Declared Classes and Instance Main Methodsが名前をSimple Source Files and Instance Main Methodsと変えて4thプレビューになりました。[プレビューの取れたJEP](https://openjdk.org/jeps/8344699)も用意されているため、Java 25では標準機能になりそうです。名前はCompact Source Files...になるみたい。こういった仕様名のこだわりもいいですね。

ﾊﾟﾌﾞﾘｯｸｽﾀﾃｨｯｸｳﾞｫｲﾄﾞﾒｲﾝの呪文から解放されるやつです。
[Javaがﾊﾟﾌﾞﾘｯｸｽﾀﾃｨｯｸｳﾞｫｲﾄﾞﾒｲﾝの呪文から解放される - きしだのHatena](https://nowokay.hatenablog.com/entry/2023/06/12/153755)

Javaでは単純なハローワールドを書くために次のようなコードが必要でした。
```java
public class Hello {
  public static void main(String[] args) {
    System.out.println("Hello Java!");
  }
}
```

これが次のように書けるようになります。
```java
void main() {
  println("Hello Java!");
}
```

プレビュー機能なので、`--enable-preview`が必要です。`javac`では`--source 23`も必要になります。ソースを直接実行する場合は`java`コマンドでも`--source 23`が必要です。
```
>more Hello.java
void main() {
  println("Hello Java!");
}

>java --enable-preview --source 23 Hello.java
ノート: Hello.javaはJava SE 23のプレビュー機能を使用します。
ノート: 詳細は、-Xlint:previewオプションを指定して再コンパイルしてください。
Hello Java!
```

`public`や`class`、`static`などのキーワードが消え、`[]`という謎の記号も消えました。
プログラムを勉強するときに、まずやりたいことは処理を書くことです。
クラスは書いた処理をうまく構成するための仕組みなので、処理が書けないうちに勉強してもあまり意味がありません。
`public`などアクセス指定はプログラムが大きくなったときに不適切な要素を使ってしまわないための仕組みなので、入門時のサンプルでは不要です。
`static`を説明するにはクラスやインスタンスの理解が必要になりますが、処理が書ける前に勉強するには早すぎます。
配列も変数を知らないうちに勉強できるものでもなく、入門時のサンプルで引数`args`を使うことはあまりありません。
その結果「よくわからないしきたり」のまま放置されがち、というか放置せざるを得ない状態で「System.out.pritlnというのは～」という説明をすることになりますが、クラス名とファイル名が違うので動かせなくてハマってそこまでたどりつけなかったりもします。

ということで、初期に学習するべきことに集中できるようにするために、次のように制約が緩和されました。

- クラスの定義が不要になる
- mainメソッドはインスタンスメソッドでよくなる
- mainメソッドの引数を省略できる
- mainメソッドがpublicじゃなくてもよくなる

「メソッドも不要でいいのでは？」となると思いますが、現状ではステートメントとメソッドを同レベルで書く仕組みがないため、新たにローカルメソッドのような仕組みが必要になり、「初期に学習するべきことに集中できるようにするため」としては影響範囲が大きいので残されています。

このあたりは、次のデザインノートにまとめられています。
https://openjdk.org/projects/amber/design-notes/on-ramp

### mainメソッドはインスタンスメソッドでもよくなる

mainメソッドにstaticをつけなくてもよくなります。そして、mainメソッドにstaticをつけなくてもいいということは、そこから呼び出すメソッドなどにもstaticをつけなくていいということになるので、少し大きめのサンプルが書きやすくもなります。

```java
public class Hello {
  public void main(String[] args) {
    foo();
  }
  void foo() {
    System.out.println("Hello");
  }
}
```

### mainメソッドの引数を省略できる / mainメソッドがpublicじゃなくてもよくなる

書かなくてよさそうなものを書かずにすむのですっきりします。
mainメソッドをprivateにすることはできません。protectedは可能です。
「public static void main(String[] args)」を何も見ずに書けるようになったときにJavaに馴染んだ満足感があったので、それがなくなるのは寂しいですが、単なるノスタルジーなのでなくていいと思います。

### クラスの定義が不要

クラスを知らなくていいことの他に、クラス名を考えなくていいとかインデントが一段浅くなるとか、中カッコが一組だけになるので間違いが減るとか、いろいろ入門がやりやすくなります。
クラスを省略してmainメソッドにインスタンスメソッドを使うと、`new Object(){}`で囲まれることになりました。
```java
new Object() {
  void main() {
    println("Hello Java");
  }
}.main();
```

ただ、クラス定義を省略する場合、ファイル名にstatic.javaなどキーワードを付けるとエラーになります。クラス定義がある場合、`java`コマンドで直接実行であれば問題ないです。

```
>java --enable-preview --source 23 static.java
static.java:3: エラー: 不正なファイル名: static
  void main() {
  ^
エラー1個
エラー: コンパイルが失敗しました
```

### java.lang.IOがstatic importされる

[`java.lang.IO`](https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/IO.html)というクラスが導入されました。このクラスには、`print`、`println`、`readln`の3つのstaticメソッドが定義されています。
クラス定義を省略して暗黙のクラスを使う場合、これらのメソッドがstaticインポートされるので、`System.out`を書く必要がなくなります。
(Java 25ではimportされない模様)

ただし、クラスを定義する場合には自動インポートは行われないので、`System.out`を書くか明示的にインポートが必要です。
先ほどの例で`System.out`を書いていましたが、`java.io.IO`をstaticインポートすることで省略できます。

```java
import static java.io.IO.*;

public class Hello {
  public void main(String[] args) {
    foo();
  }
  void foo() {
    println("Hello");
  }
}
```

### java.baseがmodule importされる

暗黙のクラスを使う場合、java.baseモジュールもimportされます。
こういうコードはimportなしで書けるようになります。

```java
void main() {
 var data = List.of("test", "data");
 println(data.stream().collect(Collectors.joining(" ")));
}
```


# API

APIの変更としてはセキュリティ系を除いて7つのJEPがありますが、新しく入ったのはJNI使用制限の準備とUnsafeでのメモリアクセス時の警告です。JEPになっていない変更が少しあります。

[472: Prepare to Restrict the Use of JNI](#472-prepare-to-restrict-the-use-of-jni)
[484: Class-File API](#484-class-file-api)
[485: Stream Gatherers](#485-stream-gatherers)
[487: Scoped Values (Fourth Preview)](#487-scoped-values-forth-preview)
[489: Vector API (Ninth Incubator)](#489-vector-api-ninth-incubator)
[498: Warn upon Use of Memory-Access Methods in sun.misc.Unsafe](#498-warn-upon-use-of-memory-access-methods-in-sunmiscunsafe)
[499: Structured Concurrency (Fourth Preview)](#499-structured-concurrency-fourth-preview)

## 小さいもの

JEPになっていないAPI変更で、動きがわかりやすいものを挙げます。

### Reader.of(CharSequence)

文字列を読み込むReaderを生成します。

```java
var r = Reader.of("hogehoge\nfoobar");
var br = new BufferedReader(r);
println(r.readLine());
// hogehoge
```

https://bugs.openjdk.org/browse/JDK-8341566

### Unicode 16サポート

Java 11でのUnicode 10サポートまではJEPがつくられていましたが、最近はだまって対応が進んでますね。
https://bugs.openjdk.org/browse/JDK-8319993

### API以外の仕様のドキュメント
External specificationsというページがAPIドキュメントに追加されています。
https://docs.oracle.com/en/java/javase/24/docs/api/external-specs.html

## [472: Prepare to Restrict the Use of JNI](https://openjdk.org/jeps/472)

Java 22の[JEP 454](https://openjdk.org/jeps/454)でForeign Memory & Function API(FFM)が標準化され、そのときFFMに関してはネイティブ呼び出しが可能かどうか指定する`--enable-native-access`などのオプションが用意されました。これらのオプションをJNIでも有効にします。

JNIの非推奨や削除は目的ではないようです。

JNIを利用する場合に、アプリケーション開発者による明示的な許可を求めることができるようにする準備です。
現状では、アプリケーション開発者による許可がないJNI呼び出しは警告が出るようです。将来的には例外を出すことがデフォルトになります。

例えば次のようにして、ネイティブアクセスを完全に許可できますが、これはFFMで導入されたものです。

```
java --enable-native-access=ALL-UNNAMED
```

`--illegal-native-access`で許可されないネイティブアクセスがあったときの処理を指定できます。`allow`は素通り。`warn`が警告で現在のデフォルトですが将来的には削除されるようです。`deny`は例外送出で今後のバージョンでのデフォルトになります。

```
--illegal-native-access=allow
--illegal-native-access=warn
--illegal-native-access=deny
```

## [484: Class-File API](https://openjdk.org/jeps/484)

Javaクラスファイルを解析、変更、生成するためのAPIです。Java 23で2ndプレビューになっていましたが、少し変更されて正式化しました。

ASMやJavassistなど、同様の機能を実装するライブラリがありますが、Javaではjavacという標準のクラスファイル生成ツールがあります。また、内部的にASMを使っている部分もあります。
ただ、ASMのように外部ライブラリを使うと、こういった外部ライブラリの新機能対応は新バージョンがリリースされるまで正式化しません。そうすると、Javaの新機能をJava内部で使いたいとき、そのASMは非公式であるかひとつ前のバージョン対応であるかということになってしまいます。
そこで、Java標準としてクラスファイル操作のAPIを提供するということになりました。

## [485: Stream Gatherers](https://openjdk.org/jeps/485)

Java 22でプレビューとして導入され、Java 23でそのまま2ndプレビューになりましたが、変更なくJava 24で正式機能になりました。

Streamでは、前の値を参照するような処理や、処理順序を前提とした処理が行えません。そのため、前の値にどんどん手を加えてリストを作ったり、移動平均をとったりといったことができませんでした。
そういった、順番を保証して前の値を踏まえた処理が行えるようにするのが、Gathererです。

デザインノートはこちら
https://cr.openjdk.org/~vklang/Gatherers.html

Collectorと同じで自前のGathererを実装するのは大変そうですが、CollectorsのようにGatherersが用意されています。ここでは、Gatherersだけ紹介します。

Streamが使えそうで使えなかった処理に、Streamが使える場合が増えそうです。

JShellを使っていますが、見やすいように改行しているので、そのとおりに入力はできません。試すときは改行せず一行で入力してください。

### windowFixed

要素を指定した個数分まとめてListにします。

```
jshell> IntStream.range(0, 10).boxed()
                 .gather(Gatherers.windowFixed(3))
                 .toList()
$1 ==> [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
```

### windowSliding

スライディングウィンドウです。指定した要素分を、要素をずらしながらとっていってListをつくります。

```
jshell> IntStream.range(0, 10).boxed()
                 .gather(Gatherers.windowSliding(3))
                 .toList()
$2 ==> [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5],
        [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9]]
```

### mapConcurrent

指定した個数分、並列に処理を行いながらmapします。このとき並列処理にはVirtualThreadが使われます。

```
jshell> IntStream.range(0, 10).boxed()
                 .gather(Gatherers.mapConcurrent(4, n -> n * 2))
                 .toList()
$3 ==> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
```

処理結果としてはmapと同じになります。

```
jshell> IntStream.range(0, 10).boxed()
                 .map(n -> n * 2)
                 .toList()
$4 ==> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
```

### scan

計算を追加していきます。

```
jshell> IntStream.range(0, 10).boxed()
                 .gather(Gatherers.scan(() -> "*", (s, n) -> s + n))
                 .toList()
$7 ==> [*0, *01, *012, *0123, *01234, *012345,
        *0123456, *01234567, *012345678, *0123456789]

```

### fold

処理順を前提としたreduce処理です。

```
jshell> IntStream.range(0, 10).boxed()
                 .gather(Gatherers.fold(() -> "*", (s, n) -> s + n))
                 .toList()
$8 ==> [*0123456789]
```

reduceではBinaryOperatorをとるので、要素と結果の型が同じになる必要があります。foldはBiFunctionをとるので、値と結果の型が同じである必要はありません。

```
jshell> IntStream.range(0, 10).boxed().reduce(0, (a, b) -> a + b)
$9 ==> 45
```

Collectors.reducingと違うのは、parallel streamでも処理順が保証されることです。

```
jshell> IntStream.range(0, 10).parallel().boxed()
                 .collect(Collectors.reducing(
                         "*", n -> "" + n, (s, t) -> s + t))
$12 ==> "*0*1*2*3*4*5*6*7*8*9"
```

あと、終端処理が必要になりますね。

```
jshell> IntStream.range(0, 10).parallel().boxed().gather(Gatherers.fold(() -> "*", (s, n) -> s + n)).findAny().get()
$14 ==> "*0123456789"
```

## [487: Scoped Values (Forth Preview)](https://openjdk.org/jeps/487)

Java 20でIncubateになりJava 21でプレビュー、そのままJava 22で2ndプレビューになりましたが、少し変更が加わって3rd Previewになりました。さらにcallWhere、runWhereメソッドがScopedValueクラスから削除されて4th Previewになりました。
[標準化のJEPのドラフト]が出ているので、Java 25で標準化しそうです

同じスレッド内で値を共有したいとき`ThreadLocal`を使いますが、値の変更が可能であったり子スレッドに値が引き継がれたり少し重いので、より限定された仕組みを提供する、ということのようです。
つまり、値を引数でひっぱりまわすのは面倒なのでグローバル変数的にstaticフィールドを使いたい程度のモチベーションで値を共有化するときに、スレッドセーフのためのThreadLocalは重すぎる、という感じですね。
たとえば次のような処理があります。

```java
void start() {
  proc1("test");
}

void proc1(String str) {
  System.out.println(str);
  proc2(str);
}

void proc2(String str) {
  System.out.println(str);
}
```

これを、全部のメソッドにいちいち引数を設定して値をひきまわるのは面倒なのでフィールドを使おう、という場合。

```java
String str;

void start() {
  str = "test";
  proc1();
}

void proc1() {
  System.out.println(str);
  proc2();
}

void proc2() {
  System.out.println(str);
}
```

これは複数スレッドから呼び出されると正しく動かないことがあります。
スレッドセーフにするために`ThreadLocal`を使っていました。

```java
final ThreadLocal<String> VAR = new ThreadLocal<>();

void start() {
  VAR.set("test");
  proc1();
}

void proc1() {
  System.out.println(VAR.get());
  proc2();
}

void proc2() {
  System.out.println(VAR.get());
}
```

しかし、引数を書いて値を持ちまわっていくのめんどいね、くらいのモチベーションで使うには`ThreadLocal`は重過ぎるので、軽量な値共有手段として`ScopedValue`が導入されます。

```java
final ScopedValue<String> VAR = new ScopedValue<>();

void start() {
  ScopedValue.where(VAR, "test")
    .run(() -> proc1());
}

void proc1() {
  System.out.println(VAR.get());
  proc2();
}

void proc2() {
  System.out.println(VAR.get());
}
```

## [489: Vector API (Ninth Incubator)](https://openjdk.org/jeps/489)

AVX命令のような、複数のデータに対する計算を同時に行う命令をJavaから利用できるようになります。
使うためには実行時やコンパイル時に`--add-modules jdk.incubator.vector`をつける必要があります。

Java 16でインキュベータとして導入されたPanamaプロジェクトの残る片割れですが、Java 23からいくつか変更されて9th Incubatorになりました。Project Valhallaのvalue classを使いたいようで、関連JEPがpreviewになるまではIncubatorのままということです。

`MemorySegment`にはバイト配列に対してVectorアクセスができていましたが、プリミティブ配列にVectorアクセスできるようになったようです。

基本的な使い方は次のようになります。

```java
import jdk.incubator.vector.*;
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256;

void vectorComputation(float[] a, float[] b, float[] c) {

    for (int i = 0; i < a.length; i += SPECIES.length()) { // SPECIES.length() = 256bit / 32bit -> 8
        VectorMask<Float> m = SPECIES.indexInRange(i, a.length); // 端数がマスクされる
                                                                 // a.lengthが11でiが8のとき最初の3つしか要素がないので [TTT.....]
		// FloatVector va, vb, vc;
        FloatVector va = FloatVector.fromArray(SPECIES, a, i, m);
        FloatVector vb = FloatVector.fromArray(SPECIES, b, i, m);
        FloatVector vc = va.mul(va).
                    add(vb.mul(vb)).
                    neg();
        vc.intoArray(c, i, m);
    }
}
```

利用できるのは次の6つの型です。それぞれに対応するVector型があって、これが基本になります。

|型|bit幅|Vector|
|--|--|--|
|byte|8|ByteVector|
|short|16|ShortVector|
|int|32|IntVector|
|long|64|LongVector|
|float|32|FloatVector|
|double|64|DoubleVector|

ただ、利用するにはVectorSpeciesが必要です。利用したいVectorにSPECIES_\*という定数が用意されているので、それを使います。\*は一度に計算するbit数ですね。

```
jshell> FloatVector.SP
SPECIES_128         SPECIES_256         SPECIES_512      
SPECIES_64          SPECIES_MAX         SPECIES_PREFERRED   
```

MAXではそのハードウェアで使える最大、PREFERREDは推奨ビット数だけど、同じになるんじゃないのかな。ここでは256bitが推奨されて、floatが8個同時に計算できるようになっていますね。

```
jshell> FloatVector.SPECIES_PREFERRED
$11 ==> Species[float, 8, S_256_BIT]
```

ハードウェアで使えるbit数は搭載CPUに依存しますが、普通のIntel/AMDであれば256、XEONとか つよつよCPUなら512かな。M1は128でした。ハードウェアでサポートされないbit数を使おうとするとソフトウェア処理になるので遅くなります。

実際のVectorはfrom*というメソッドで取得します。fromArray、fromByteArray、fromByteBufferが用意されています。インキュベータに入る前はfromValuesがあったのですが、なくなってますね。

Vectorを得られたら、用意されたメソッドで計算します。ひととおりの算術命令はあります。

```
jshell> va.
abs()                    add(                     addIndex(
bitSize()                blend(                   broadcast(
byteSize()               castShape(               check(
compare(                 compress(                convert(
convertShape(            div(                     elementSize()
elementType()            eq(                      equals(
expand(                  fma(                     getClass()
hashCode()               intoArray(               intoMemorySegment(
lane(                    lanewise(                length()
lt(                      maskAll(                 max(
min(                     mul(                     neg()
notify()                 notifyAll()              pow(
rearrange(               reduceLanes(             reduceLanesToLong(
reinterpretAsBytes()     reinterpretAsDoubles()   reinterpretAsFloats()
reinterpretAsInts()      reinterpretAsLongs()     reinterpretAsShorts()
reinterpretShape(        selectFrom(              shape()
slice(                   species()                sqrt()
sub(                     test(                    toArray()
toDoubleArray()          toIntArray()             toLongArray()
toShuffle()              toString()               unslice(
viewAsFloatingLanes()    viewAsIntegralLanes()    wait(
withLane(  
```

ところで、こういったメソッド呼び出しの内部でAVX命令などを呼び出すのでは遅くなるんではという気がしますが、実際にはJVM intrinsicsという仕組みでJITコンパイラがこれらのメソッド呼び出しをネイティブ関数呼び出しに置き換えます。

## [498: Warn upon Use of Memory-Access Methods in sun.misc.Unsafe](https://openjdk.org/jeps/498)

Foreign memory APIが正式化されて代替手段が整ったことで、`sun.misc.Unsafe`でのメモリアクセスAPIがJava 23で非推奨になりました。
今回のJava 24では実行時警告が出るようになっています。
Java 25 LTSではそのまま残り、Java 26以降で例外が発生するようになり、その後で削除されるという流れが予定されています。

## [499: Structured Concurrency (Fourth Preview)](https://openjdk.org/jeps/499)

Java 19でIncubatorとして含まれていましたが、Java 21、22、23から変更なく4th Previewになりました。ただ、[5th previewのJEP](https://openjdk.org/jeps/8340343)が用意されているので、Java 25 LTSでの正式化はなさそうです。

並列処理では、複数の処理を実行するときに、両方が終われば正常終了とか、どちらか片方が終われば終了だとか、どちらか一方でも例外が発生したら終了だとか、同時に行う処理で連動することがあります。
しかし、これを既存の`join`や`wait`などで制御しようとすると、実際には`join`から`wait`へのGo Toを書くようなコードになって、処理が追えなくなります。
そこで導入されるのが構造化並列性といいます。
こんな感じ。詳しくはあとで書きます！(Java 19のときから言ってる・・・)

```java
Response handle() throws ExecutionException, InterruptedException {
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        Subtask<String>  user  = scope.fork(() -> findUser());
        Subtask<Integer> order = scope.fork(() -> fetchOrder());

        scope.join()            // Join both forks
             .throwIfFailed();  // ... and propagate errors

        // Here, both forks have succeeded, so compose their results
        return new Response(user.get(), order.get());
    }
}
```

`Subtask`は`Supplier`を継承しているので、`Supplier`として扱うほうがいいかもしれません。

# JVM

JVMの変更として、セキュリティ関連以外では7つJEPが導入されています。

[404: Generational Shenandoah (Experimental)](#404-generational-shenandoah-experimental)
[450: Compact Object Headers (Experimental)](#450-compact-object-headers-experimental)
[475: Late Barrier Expansion for G1](#475-late-barrier-expansion-for-g1)
[479: Remove the Windows 32-bit x86 Port](#479-remove-the-windows-32-bit-x86-port)
[483: Ahead-of-Time Class Loading & Linking](#483-ahead-of-time-class-loading--linking)
[490: ZGC: Remove the Non-Generational Mode](#490-zgc-remove-the-non-generational-mode)
[491: Synchronize Virtual Threads without Pinning](#491-synchronize-virtual-threads-without-pinning)

## [404: Generational Shenandoah (Experimental)](https://openjdk.org/jeps/404)

Shenandoah GCに世代別GCを試験機能として導入します。

## [450: Compact Object Headers (Experimental)](https://openjdk.org/jeps/450)

JVM内でオブジェクトを保持するときに、64bitプラットフォームでは、96-128bit、つまり12-16バイトをヘッダとして使っているのですが、これを64bit、8バイトにするものです。
このことによってヒープサイズは減り、データの局所性があがります。キャッシュに乗りやすくなって性能があがるということですね。

現在はデフォルトで無効で、`-XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders`というVMオプションをつけることで有効化されます。
今後、デフォルト有向にしたあと、現在の96-128bitヘッダに関するコードを削除する方向です。

## [475: Late Barrier Expansion for G1](https://openjdk.org/jeps/475)

G1GCでのバリア処理をC2コンパイラの初期ではなく最適化のあとにすることで、オーバーヘッドが減り効率化し、G1GCの実装を簡素化します。

## [479: Remove the Windows 32-bit x86 Port](https://openjdk.org/jeps/479)

Windows 32bit対応は[Java 21でDeprecate](https://openjdk.org/jeps/449)になったため、すでにビルドをリリースしているディストリビューションはないので、今回はコードの削除です。

## [483: Ahead-of-Time Class Loading & Linking](https://openjdk.org/jeps/483)

クラスがJVMにロードされた状況を保存し使いまわすことで、JVMの起動やピーク性能に至る時間を短くします。

まずは、アプリケーションを一旦起動してクラスのローディングデータを記録します。ここではデータがapp.aotconfファイルに記録されます。
```sh
java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconf \
       -cp app.jar com.example.App ...
```

そのあと、記録データからキャッシュファイルを作成します。ここではapp.aotファイルにキャッシュが作成されます。
```sh
java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconf \
       -XX:AOTCache=app.aot -cp app.jar
```

そして実行時にキャッシュを利用します。

```sh
java -XX:AOTCache=app.aot -cp app.jar com.example.App ...
```

QuarkusでRESTだけいれたプロジェクトを試すと、こんな感じのファイルができました。
```
2025/03/21  11:23        35,192,832 code.aot
2025/03/21  11:22           578,141 code.aotconf
```

起動時間を雑にみてみると、1秒かかっていたのが0.35秒になる感じ。かなり速いですね。
Spring Bootだと2.85秒かかっていたのが2.60秒くらい、コンスタントに0.2秒弱速くなる感じかな。Spring Boot自体の起動が遅いので、効果は体感できないですね。。

また、QuarkusやSpring Bootではユーザーアプリケーションや拡張機能がカスタムクラスローダーで読み込まれますが、JEP-483では標準クラスローダーだけがサポートされるので、そういった部分はキャッシュされません。
今後の課題となっています。

## [490: ZGC: Remove the Non-Generational Mode](https://openjdk.org/jeps/490)

Java 23で[ZGCでの世代別GCがデフォルトになっていました](https://openjdk.org/jeps/474)が、非世代別モードが削除されます。

## [491: Synchronize Virtual Threads without Pinning](https://openjdk.org/jeps/491)

仮想スレッドでsynchrinizedブロックを動かすとき、プラットフォームスレッドに仮想スレッドをピン止めしていました。そうすると、そのプラットフォームスレッドは他の仮想スレッドで利用できず、プラットフォームスレッドの枯渇やデッドロックが発生しました。
ピン止めしないようにすることで、そういった問題を改善します。

恐らく、こういった問題が起きにくくなります。
[Netflixが仮想スレッドを採用：パフォーマンスと落とし穴に関するケーススタディ - InfoQ](https://www.infoq.com/jp/news/2024/10/netflix-performance-case-study/)

# Security

今回からセキュリティ関連をわけました。量子耐性暗号としてモジュール格子暗号に関する機能が追加されたのが見どころ

[478: Key Derivation Function API (Preview)](#478-key-derivation-function-api-preview)
[486: Permanently Disable the Security Manager](#486-permanently-disable-the-security-manager)
[496: Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism](#496-quantum-resistant-module-lattice-based-key-encapsulation-mechanism)
[497: Quantum-Resistant Module-Lattice-Based Digital Signature Algorithm](#497-quantum-resistant-module-lattice-based-digital-signature-algorithm)

## [478: Key Derivation Function API (Preview)](https://openjdk.org/jeps/478)

暗号鍵を安全に生成するためのキー導出機能(KDF)のAPIです。
Java 21で[JEP 452](https://openjdk.org/jeps/452) として導入されたKey Encapsulation Mechanism(KEM)で、公開鍵暗号のもとで安全に共有鍵を交換する仕組みが導入されました。それにあわせて、今回のKDFの導入で、ハイブリッド公開鍵暗号の実装の準備が整います。

ハイブリッド公開鍵暗号では、短いセッション鍵を一旦生成してKEMでカプセル化して送信、共有されたセッション鍵を使ってお互いにKDFで対照鍵を生成してデータ交換に利用、という流れになります。

## [486: Permanently Disable the Security Manager](https://openjdk.org/jeps/486)

セキュリティマネージャーはAppletやJava Web Startのために使われてましたが、サーバーサイドでは全く使われず、Java 17の[JEP 411](https://openjdk.org/jeps/411) で非推奨になっていました。
それにもかかわらず、ネットワークやIO、Swingなどではセキュリティマネージャー対応のコードが必要で、メンテナンスが大変ということで、今回は完全に無効にして、セキュリティマネージャー依存のコードを削除できるようにします。
`-Djava.security.manager`でエラーが出るようになり、`System.setSecurityManager`は例外を投げます。

## [496: Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism](https://openjdk.org/jeps/496)

量子コンピュータではShorのアルゴリズムを使って素因数分解が高速にできてしまうので、素因数分解の計算困難性を利用するRSAなどは量子耐性がないと言われています。
そこで、量子耐性のあるモジュール格子暗号を使ってキー交換をする仕組みが導入されます。

また、これらの量子耐性アルゴリズムは、メンテナンスされているLTSバージョンにバックポートされるようです。
https://xtech.nikkei.com/atcl/nxt/column/18/00001/10396/:title

## [497: Quantum-Resistant Module-Lattice-Based Digital Signature Algorithm](https://openjdk.org/jeps/497)

量子耐性のあるモジュール格子暗号を使って電子署名を行うアルゴリズムが導入されます。

# ツール
ツール関連ではJEPはひとつですが、JEPになっていない変更がちょこちょこあります。
[493: Linking Run-Time Images without JMODs](#493-linking-run-time-images-without-jmods)

## JEPになってない変更

### javaコマンドからいくつかのオプションが削除
次のオプションが削除されています。
-t, -tm, -Xfuture, -checksource, -cs, -noasyncgc
https://bugs.openjdk.org/browse/JDK-8339918

### javaコマンドのいくつかのオプションが非推奨に
次のオプションが非推奨になっています。Java 25で削除かな。
-verbosegc, -noclassgc, -verify, -verifyremote, -ss, -ms, -mx

これらは省略形なので、ちゃんと書けという話っぽい

|*Deprecated|*Replacement|
|---|---|
|-verbosegc|-verbose:gc|
|-noclassgc|-Xnoclassgc|
|-verify|-Xverify:all|
|-verifyremote|-Xverify:remote|
|-ss|-Xss|
|-ms|-Xms|
|-mx|-Xmx|

https://bugs.openjdk.org/browse/JDK-8286851

### JARツールで展開先を指定可能に
`-C`と`-dir`オプションが追加されて、展開先を指定できるようになりました。

```
jar xf foo.jar -C /tmp/bar/
```

```
jar --extract --file foo.jar --dir /tmp/bar/
```

https://bugs.openjdk.org/browse/JDK-8173970

### JARツールでファイル上書きを抑制

`k`と`-keep`オプションが指定されて、ファイル上書きしないようにできます。

```
jar xkf foo.jar
```

```
jar --extract --keep-old-files --file foo.jar
```
https://bugs.openjdk.org/browse/JDK-8335912

### jstatdが非推奨に
`jstatd`コマンドが非推奨になっています。Java 25で削除かな

https://bugs.openjdk.org/browse/JDK-8327793

### jrunscriptコマンドが非推奨に
`jrunscript`コマンドが非推奨になっています。これもJava 25で削除かな。
https://bugs.openjdk.org/browse/JDK-8341134

## [493: Linking Run-Time Images without JMODs](https://openjdk.org/jeps/493)

jlinkコマンドでカスタムJREを作るときにjmodファイルを使わないようにすることで、JDKサイズを25%削減する、らしい。
デフォルトは無効で、ディストリビューションによって有効にされるという話。
未検証。あとで書く

# JDK

Windowsでのx86 32bitがコードまで消されました。次は他のプラットフォームも。
[501: Deprecate the 32-bit x86 Port for Removal](#501-deprecate-the-32-bit-x86-port-for-removal)

## [501: Deprecate the 32-bit x86 Port for Removal](https://openjdk.org/jeps/501)

ということで、32bit x86向けのコードを消すために、一旦非推奨になります。
各ディストリビューションでも、32bit版の提供が終わったようです。
[JEP-503](https://openjdk.org/jeps/503)で32bit x86コードは削除されます。
arm 32bitは非推奨になったわけでもないけど、いつのまにか各ディストリビューションから消えていますね。UbuntuなどでRaspberry Pi用に32bit ARMのバイナリがメンテナンスされてるっぽい。