Java 25が2025/9/16にリリースされました。
Java 25 / JDK 25: General Availability
Oracle Releases Java 25
The Arrival of Java 25
LTSで、Java 21からの変更も多いので、長く使われるバージョンになると思います。
今回はmainメソッドの簡略化とモジュール単位でのimportが正式化されたことが大きいですね。というか、それ以外は大きな影響がなさそうです。
IO.printlnと書けるようになったことは、補完が効かない環境でコードを書くときにいいです。
あとSoundClipは雑ゲームを作るときに便利。
JDKをインストールせずに言語やライブラリの新機能を試したい場合にはJava Playgroundが便利です。
https://dev.java/playground/
イベント
新機能解説のイベントを福岡で開催します。
10/17(金)に福岡エンジニアカフェで19時から行います。
Java 25リリースイベント@福岡 - connpass
資料
詳細はこちら
JDK 25 Release Notes
Java SE 25 Platform JSR 400
OpenJDK JDK 25 GA Release
APIドキュメントはこちら
Overview (Java SE 25 & JDK 25)
追加されたAPIまとめはこちら
https://docs.oracle.com/en/java/javase/25/docs/api/new-list.html
APIの差分はこちら。
https://cr.openjdk.org/~iris/se/25/build/latest/java-se--jdk-24-ga--jdk-25%2B36/
ディストリビューション
MacやLinuxでのインストールにはSDKMAN!をお勧めします。
Oracle OpenJDK以外に無償で商用利用できるディストリビューションとしては、次のようなものがあります。
- Oracle JDK
- Eclipse Temurin
- Liberica JDK
- Amazon Corretto 25
- SapMachine
- Azul Zulu
- Microsoft Build of OpenJDK
※ Microsoftは未リリース
どのディストリビューションを使えばいいかわからないときは、とりあえずEclipse Temurinが無難です。
アップデートは10月に25.0.1が、1月に25.0.2がリリースされることになります。
Oracle JDKの無償ライセンスであるNFTCは次期LTSまで有効で、それ以降のアップデートでは有償のOTNに切り替わるので注意が必要です。
開発組織
関わった開発組織は、こんな感じになってます。比率は大きくないですが、日本企業ではFujitsuとNTT Dataが貢献を続けています。
JEP
大きめの変更はJEPでまとまっています。
https://openjdk.org/projects/jdk/25/
今回は18個のJEPが取り込まれました。正式採用は12個です。プレビューからの正式化はScoped ValuesとModule Import Declarations、Compact Source Files、Flexible Constructor Bodiesの4つで、後者3つによってコードが書きやすくなっています。プレビューは6個で、そのうち3つが新たに入ったものです。
今回は次の5つのカテゴリにまとめてます
470: PEM Encodings of Cryptographic Objects (Preview)
502: Stable Values (Preview)
503: Remove the 32-bit x86 Port
505: Structured Concurrency (Fifth Preview)
506: Scoped Values
507: Primitive Types in Patterns, instanceof, and switch (Third Preview)
508: Vector API (Tenth Incubator)
509: JFR CPU-Time Profiling (Experimental)
510: Key Derivation Function API
511: Module Import Declarations
512: Compact Source Files and Instance Main Methods
513: Flexible Constructor Bodies
514: Ahead-of-Time Command-Line Ergonomics
515: Ahead-of-Time Method Profiling
518: JFR Cooperative Sampling
519: Compact Object Headers
520: JFR Method Timing & Tracing
521: Generational Shenandoah
18個というのは、半年リリースになって以降としては、前回の24個についで多いですね。
前回LTSのJava 21でその前のJava 17からのJEP数が37個だったことを考えると、今回はJava 21から66個のJEPが取り込まれているので、結構な機能改善がされていることになります。ただし、同じ内容のJEPがpreviewとして何度も入っているので、その分は差し引く必要があります。
言語機能
言語機能の変更としては新しいものはないですが、いままでPreviewだった機能が正式化されています。
507: Primitive Types in Patterns, instanceof, and switch (Third Preview)
511: Module Import Declarations
512: Compact Source Files and Instance Main Methods
513: Flexible Constructor Bodies
507: Primitive Types in Patterns, instanceof, and switch (Third Preview)
Java 23でプレビューとして入りましたが、変更なく3rdプレビューになりました。
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
実数型を整数型で判定するときは、精度落ちするかどうかが判定されます。
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 -> "おりた";
}
511: Module Import Declarations
Java 23でプレビューとして入り、Java 25で正式化されました。
モジュールごとのimportができるようになっています。java.baseをimportしたらだいたいいけるってなりそうです。あと、外部ライブラリのモジュール対応が進みそうです。
Java 23のときからの変更で、依存のあるモジュールもimportされるようになりました。そのためjava.seをimportすると、Java SEのすべてのAPIがimportされます。
次のようにすると、java.baseモジュールに属するjava.utilもjava.ioもimportされることになります。
import module java.base;
JShellでもデフォルトでjava.baseがimportされるようになります。
例えば、java.timeはimportされていなかったのでLocalDateをそのまま使うとエラーになります。
>jshell
| JShellへようこそ -- バージョン24
| 概要については、次を入力してください: /help intro
jshell> LocalDate.now()
| エラー:
| シンボルを見つけられません
| シンボル: 変数 LocalDate
| 場所: クラス
| LocalDate.now()
| ^-------^
Java 25ではimportなしでLocalDateが使えます。
>jshell
| JShellへようこそ -- バージョン25
| 概要については、次を入力してください: /help intro
jshell> LocalDate.now()
$1 ==> 2025-09-16
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
512: Compact Source Files and Instance Main Methods
Java 24で4thプレビューになっていたSimple Source Files and Instance Main MethodsがCompact Souce Files...と名前を変えて正式化されました。こういった仕様名のこだわりもいいですね。
パブリックスタティックヴォイドメインの呪文から解放されるやつです。
Javaがパブリックスタティックヴォイドメインの呪文から解放される - きしだのHatena
Javaでは単純なハローワールドを書くために次のようなコードが必要でした。
public class Hello {
public static void main(String[] args) {
System.out.println("Hello Java!");
}
}
これが次のように書けるようになります。
void main() {
IO.println("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をつけなくていいということになるので、少し大きめのサンプルが書きやすくもなります。
public class Hello {
public void main(String[] args) {
foo();
}
void foo() {
IO.println("Hello");
}
}
mainメソッドの引数を省略できる / mainメソッドがpublicじゃなくてもよくなる
書かなくてよさそうなものを書かずにすむのですっきりします。
mainメソッドをprivateにすることはできません。protectedは可能です。
「public static void main(String[] args)」を何も見ずに書けるようになったときにJavaに馴染んだ満足感があったので、それがなくなるのは寂しいですが、単なるノスタルジーなのでなくていいと思います。
クラスの定義が不要
クラスを知らなくていいことの他に、クラス名を考えなくていいとかインデントが一段浅くなるとか、中カッコが一組だけになるので間違いが減るとか、いろいろ入門がやりやすくなります。
void main() {
IO.println("Hello Java");
}
クラスを省略してmainメソッドにインスタンスメソッドを使うとき、Java 24でのプレビューまではnew Object(){}
で囲まれることになっていましたが、正式リリースのJava 25でこの仕様は削られています。
new Object() {
void main() {
IO.println("Hello Java");
}
}.main();
現状では、ファイル名と同じクラスが定義されてそのメンバになるようです。
クラス定義を省略する場合、ファイル名にstatic.javaなどキーワードを付けるとエラーになります。クラス定義がある場合、java
コマンドで直接実行であれば問題ないです。
>java static.java
static.java:3: エラー: 不正なファイル名: static
void main() {
^
エラー1個
エラー: コンパイルが失敗しました
java.baseがmodule importされる
暗黙のクラスを使う場合、java.baseモジュールもimportされます。
こういうコードはimportなしで書けるようになります。
void main() {
var data = List.of("test", "data");
IO.println(data.stream().collect(Collectors.joining(" ")));
}
513: Flexible Constructor Bodies
Java 22でStatements before superだったものがJava 23で名前が変わり、大きな変更なくJava 25で正式化しました。
スーパークラスのコンストラクタ呼び出しの前にthis
を使わないステートメントが書けるようになります。
例えばListを2つコンストラクタにとるクラスがあるとします。
class Foo {
Foo(List l1, List l2) {
}
}
このコンストラクタを継承するとき、Listをふたつ渡す必要があります。
class Bar extends Foo {
Bar() {
super(List.of("abc", "def"), List.of("abc", "def"));
}
}
同じものを渡しているので共通化したいですが、super
の前でステートメントを実行できないので迂回を考える必要があります。
class Bar extends Foo {
private Bar(List l) {
super(l, l);
}
Bar() {
this(List.of("abc", "def"));
}
}
これを、次のように書けるようになります。
class Bar extends Foo {
Bar() {
var param = List.of("abc", "def");
super(param, param);
}
このように、パラメータの検証や構築、共有がある場合に、自然なコードで書けるようになります。
次のようなフィールドのアクセスをsuper
の前に行うとエラーになります。
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");
^
API
APIの変更としてはセキュリティ系を除いて4つのJEPがありますが、新しく入ったのはStable Valueです。Scoped Valueが正式化されています。またJEPになっていない変更が結構あります。
502: Stable Values (Preview)
505: Structured Concurrency (Fifth Preview)
506: Scoped Values
508: Vector API (Tenth Incubator)
小さいもの
JEPになっていないAPI変更で、動きがわかりやすいものを挙げます。
java.lang.IO
java.lang.IO
というクラスが導入されました。このクラスには、print
、println
、readln
の3つのstaticメソッドが定義されています。
これを利用してSystem.out.println
ではなくIO.println
を使って次のようにHello worldが書けます。
void main() {
IO.println("Hello");
}
static importを行えば、println
だけで文字列出力ができます。
import static java.lang.IO.*;
void main() {
println("Hello");
}
JShellやメモ帳など、soutのような補完の効かない環境でのコード入力に特に便利です。
また、System.io
がInputStream
を返していたため、入力操作にはIOException
への対処が必要になっていましたが、IO.readln
ではIOError
を投げるため例外処理を省略できます。
javax.sound.SoundClip
次のようにしてWAVファイルを再生できます。
var file = new File("sound.wav");
var clip = SoundClip.createSoundClip(file);
clip.play();
いままで、音声ファイルを再生する一番手軽な手段はApplet APIのAudioClipだったのですが、Applet APIはJava 17でDeprecated になり、Java 26で削除の予定です。
そこで手軽に音声を再生するための代替手段として、SoundClipクラスが導入されました。
[JDK-8356049] Need a simple way to play back a sound clip - Java Bug System
標準では非圧縮PCMにしか対応していないので、MP3を再生するにはSPIを導入する必要があります。
Java 25でMP3を再生する - きしだのHatena
ゲーム効果音に使うときの注意点はこちら。
Java 25のSoundClipでゲームに効果音をつける - きしだのHatena
Reader.readAllLines / readAllAsString
ReaderクラスにList<String>
を返すreadAllLines
とString
を返すreadAllAsString
のふたつのメソッドが追加されました。
public List<String> readAllLines() throws IOException
public String readAllAsString() throws IOException
[JDK-8354724] Methods in java.io.Reader to read all characters and all lines - Java Bug System
CharSequence.getChars(int, int, char[], int)
文字列を文字単位で処理するときに、範囲指定で一括してchar配列に取り込むメソッドが用意されました。
Reader.of
の実装を簡素化することが目的のようですが、同様に文字列を文字単位で処理する場合に効率がよくなるようです。
[JDK-8343110] Add getChars(int, int, char[], int) to CharSequence and CharBuffer - Java Bug System
BodyHandlers.limiting / BodySubscribers.limiting
HttpClientでの送受信データをBodyHandler / BodySubscriberで処理をするときに上限バイト数を指定しやすくなります。
次のようにすると、250バイトだけ文字列として取得するということになります。
client.send(request, BodyHandlers.limiting(BodyHandlers.ofString(), 250))
ForkJoinPoolのScheduledFuture対応
ForkJoinPool
にScheduledFuture
対応のメソッドが追加されました。
- cancelDelayedTasksOnShutdown()
- getDelayedTaskCount
- schedule(Runnable, long, TimeUnit)
- schedule(Callable, long, TimeUnit)
- scheduleAtFixedRate(Runnable, long, long, TimeUnit)
- scheduleWithFixedDelay(Runnable, long, long, TimeUnit)
- submitWithTimeout(Callable, long, TimeUnit, Consumer)
[JDK-8319447] Improve performance of delayed task handling - Java Bug System
StrictMath
毎回StrictMathにちょっとずつメソッドが追加されていますが、今回も7つのメソッドが追加されました。
- powExact(int, int)
- powExact(long, int)
- unsignedMultiplyExact(int, int)
- unsignedMultiplyExact(long, int)
- unsignedMultiplyExact(long, long)
- unsignedPowExact(int, int)
- unsignedPowExact(long, int)
502: Stable Values (Preview)
Javaでは、不変な値を保持する仕組みとしてfinal
があります。
final Logger logger = Logger.create();
しかし、クラスやインスタンスの初期化時にすべての不変値を初期化してしまうと、起動が遅くなったりネットやファイルへのアクセスが集中することにもなります。
そこで、値が必要になったときに初期化したいということが多くあります。
そのような場合、アクセッサを介して値を得るようにすることで、遅延初期化を実現します。
private Logger logger;
Logger getLogger() {
if (logger == null) {
Logger.create();
}
return logger;
}
けれども、このようにするとフィールドからfinal
が外れることでJVMでの最適化が効きにくくなります。また、マルチスレッドを考慮すると少し複雑なコードが必要になります。もしくは、マルチスレッドで初期化が多重に行われても問題ないかどうか判断して、実質的な問題がなければそのままスレッドセーフではないコードを使うことになります。
そういった場合に使えるのが今回導入されるStableValue
です。
private final StableValue<Logger> logger = StableValue.of();
Logger getLogger() {
return logger.orElseSet(() -> Logger.create());
}
このときorElseSet
に指定した初期化コードは1度だけしか呼び出されないことがStableValue
によって保証され、スレッドセーフになります。また、JVMでの最適化も行われるようになります。
アクセッサメソッドの定義が面倒ということもあります。その場合はsupplier
が使えます。
final Supplier<Logger> logger = StableValue.supplier(() -> Logger.create());
不変値の集合を扱いたいときはlist
が使えます。
final List<Connection> pool = StableValue.list(10, idx -> new Connection(idx + "th connection");
こうするとpool.get(3)
などとしてリストにアクセスすると、初回にそのインデックスに対応する値が初期化されるようになります。
StableValue
はプレビューなので、使うときには--enable-preview
が必要です。
505: Structured Concurrency (Fifth Preview)
Java 19でIncubatorとして含まれていましたが、Java 24で4th Previewになり、Java 25ではStructuredTaskScope
がクラスからインタフェースになり、open
ファクトリメソッドを使ってインスタンスを得るようになりつつ5thプレビューになりました。
並列処理では、複数の処理を実行するときに、両方が終われば正常終了とか、どちらか片方が終われば終了だとか、どちらか一方でも例外が発生したら終了だとか、同時に行う処理で連動することがあります。
しかし、これを既存のjoin
やwait
などで制御しようとすると、実際にはjoin
からwait
へのGo Toを書くようなコードになって、処理が追えなくなります。
そこで導入されるのが構造化並列性といいます。
こんな感じ。詳しくはあとで書きます!(Java 19のときから言ってる・・・)
Response handle() throws InterruptedException {
try (var scope = StructuredTaskScope.open()) {
Subtask<String> user = scope.fork(() -> findUser());
Subtask<Integer> order = scope.fork(() -> fetchOrder());
scope.join() // Join both forks, propagatiton exceptions
// Both subtasks have succeeded, so compose their results
return new Response(user.get(), order.get());
}
}
Subtask
はSupplier
を継承しているので、Supplier
として扱うほうがいいかもしれません。
506: Scoped Values
Java 20でIncubateになりJava 21でプレビュー、Java 24からはScopedValue.orElse
でnullを受け付けなくなって正式化しました。
同じスレッド内で値を共有したいときThreadLocal
を使いますが、値の変更が可能であったり子スレッドに値が引き継がれたり少し重いので、より限定された仕組みを提供する、ということのようです。
つまり、値を引数でひっぱりまわすのは面倒なのでグローバル変数的にstaticフィールドを使いたい程度のモチベーションで値を共有化するときに、スレッドセーフのためのThreadLocalは重すぎる、という感じですね。
たとえば次のような処理があります。
void start() {
proc1("test");
}
void proc1(String str) {
IO.println(str);
proc2(str);
}
void proc2(String str) {
IO.println(str);
}
これを、全部のメソッドにいちいち引数を設定して値をひきまわるのは面倒なのでフィールドを使おう、という場合。
String str;
void start() {
str = "test";
proc1();
}
void proc1() {
IO.println(str);
proc2();
}
void proc2() {
IO.println(str);
}
これは複数スレッドから呼び出されると正しく動かないことがあります。
スレッドセーフにするためにThreadLocal
を使っていました。
final ThreadLocal<String> VAR = new ThreadLocal<>();
void start() {
VAR.set("test");
proc1();
}
void proc1() {
IO.println(VAR.get());
proc2();
}
void proc2() {
IO.println(VAR.get());
}
しかし、引数を書いて値を持ちまわっていくのめんどいね、くらいのモチベーションで使うにはThreadLocal
は重過ぎるので、軽量な値共有手段としてScopedValue
が導入されます。
final ScopedValue<String> VAR = new ScopedValue<>();
void start() {
ScopedValue.where(VAR, "test")
.run(() -> proc1());
}
void proc1() {
IO.println(VAR.get());
proc2();
}
void proc2() {
IO.println(VAR.get());
}
508: Vector API (Tenth Incubator)
AVX命令のような、複数のデータに対する計算を同時に行う命令をJavaから利用できるようになります。
使うためには実行時やコンパイル時に--add-modules jdk.incubator.vector
をつける必要があります。
Java 16でインキュベータとして導入されたPanamaプロジェクトの残る片割れですが、Java 24から実装の変更などが入って10th Incubatorになりました。Project Valhallaのvalue classを使いたいようで、関連JEPがpreviewになるまではIncubatorのままということです。
基本的な使い方は次のようになります。
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コンパイラがこれらのメソッド呼び出しをネイティブ関数呼び出しに置き換えます。
JVM
JVMの変更として、セキュリティ関連以外では5つJEPが導入されています。
503: Remove the 32-bit x86 Port
514: Ahead-of-Time Command-Line Ergonomics
515: Ahead-of-Time Method Profiling
519: Compact Object Headers
521: Generational Shenandoah
503: Remove the 32-bit x86 Port
Java 24ではWindows用の32-bit x86コードが削除されていました。
479: Remove the Windows 32-bit x86 Port
また、他のプラットフォーム向けの32-bit x86は非推奨にされています。
JEP 501: Deprecate the 32-bit x86 Port for Removal
ということで、今回はコードが消されることになりました。
Arm用の32bitコードは残るはず。Raspberry Piで使われているので。
514: Ahead-of-Time Command-Line Ergonomics
Java 24で取り込まれたJEP 483: Ahead-of-Time Class Loading & Linkingでクラスのローディングデータを使いまわすことでの起動時間短縮が可能になりました。
けども、この際のコマンドラインオプションが結構めんどくさい。
まず記録。
java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconf \
-cp app.jar com.example.App ...
それからキャッシュファイルの作成
java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconf \
-XX:AOTCache=app.aot -cp app.jar
そして実行時にキャッシュを利用です。
java -XX:AOTCache=app.aot -cp app.jar com.example.App ...
この、準備に使う最初の2つが統合されて、次のように書けるようになります。
java -XX:AOTCacheOutput=app.aot -cp app.jar com.example.App ...
515: Ahead-of-Time Method Profiling
JEP 483では、クラスデータをキャッシュして使いまわせるようにしていましたが、メソッドプロファイルデータも同時に使いまわせるようにしてピークパフォーマンスまでの時間を短縮します。
特別なコマンドオプションは導入されていません。
519: Compact Object Headers
JVM内でオブジェクトを保持するときに、64bitプラットフォームでは、96-128bit、つまり12-16バイトをヘッダとして使っているのですが、これを64bit、8バイトにするものです。
このことによってヒープサイズは減り、データの局所性があがります。キャッシュに乗りやすくなって性能があがるということですね。
現在はデフォルトで無効で、-XX:+UseCompactObjectHeaders
というVMオプションをつけることで有効化されます。Java 24で必要だった-XX:+UnlockExperimentalVMOptions
は正式化により不要になりました。
今後、デフォルト有効にしたあと、現在の96-128bitヘッダに関するコードを削除する方向です。
521: Generational Shenandoah
Java 24で試験導入されていたShenandoah GCでの世代別GCが正式化しました。
Security
セキュリティ、暗号関係では次の2つのJEPが導入されています。
470: PEM Encodings of Cryptographic Objects (Preview)
510: Key Derivation Function API
470: PEM Encodings of Cryptographic Objects (Preview)
暗号鍵や証明書などの暗号オブジェクトを送受信するための表現形式として、Privacy-Enhanced Mail(PEM)形式がよく使われています。PEM形式は名前のとおり、メール用に設計されましたが、他の用途にも使われるようになっています。
このJEPで、PEM形式へのエンコードやデコードを行うAPIを定義します。
510: Key Derivation Function API
暗号鍵を安全に生成するためのキー導出機能(KDF)のAPIです。
Java 21でJEP 452 として導入されたKey Encapsulation Mechanism(KEM)で、公開鍵暗号のもとで安全に共有鍵を交換する仕組みが導入されました。それにあわせて、今回のKDFの導入で、ハイブリッド公開鍵暗号の実装の準備が整います。
ハイブリッド公開鍵暗号では、短いセッション鍵を一旦生成してKEMでカプセル化して送信、共有されたセッション鍵を使ってお互いにKDFで対照鍵を生成してデータ交換に利用、という流れになります。
ツール
ツール関連ではJVM Flight Recorder関連の変更が3つ入っています。
509: JFR CPU-Time Profiling (Experimental)
518: JFR Cooperative Sampling
520: JFR Method Timing & Tracing
509: JFR CPU-Time Profiling (Experimental)
Linux環境でカーネルのCPUタイマーを使うことで、CPU時間プロファイリングが行えるようにします。
Linux専用の機能になりますが、将来的に他の環境にも対応する予定です。
サンプリング結果はjdk.CPUTimeSample
イベントとして記録されます。
次のようにして有効にします。
$ java -XX:StartFlightRecording=jdk.CPUTimeSample#enabled=true,filename=
518: JFR Cooperative Sampling
JDK Flight Recoderでは、数ミリ秒ごとといった固定間隔で実行スタックをサンプリングすることでプロファイルを行っています。
そのため、JFRのサンプラースレッドは、対象スレッドを一時停止してスタックフレームを解析します。JDKではスタックフレームを補助するメタデータを提供しますが、これはセーフポイントと呼ばれる位置で停止した場合に限られます。かといって、セーフポイントだけでスタックフレームの解析を行うと、頻繁に実行されるコードがセーフポイント付近にない場合に精度が落ちるセーフポイントバイアス問題が発生します。
そういった問題が発生しないよう非同期にサンプリングを行うと、セーフポイント以外で停止した場合には非効率であったりJVMがクラッシュすることもあります。
このJEPでは、サンプリング機構を設計しなおしてセーフポイントのみでサンプリングを行うようにしています。セーフポイントバイアスの問題を回避するために、サンプリングタイミングではプログラムカウンタとスタックポインタをサンプル要求としてキューに記録し、次にセーフポイントに到達したときにスタックフレームの解析を行うことで、安全でバイアスなくサンプリングが行われるようにします。
520: JFR Method Timing & Tracing
JDK Flight Recoderにjdk.MethodTiming
とjdk.MethodTrace
の2種類のイベントを導入して、メソッドの時間やトレースを記録できるようにします。