2026/09/15

JEPでは語れないJava 27

このエントリーをはてなブックマークに追加

毎度おなじみ、半年ぶりのJavaのアップデートです。

Java 27はLTSの間の中間のバージョンからか導入された機能も裏方的なものが多く。APIの変更も少ししかありません。まぁ、そんなもんです。

Java 27のStandard JEPは4つ。

  • JEP 523: Make G1 the Default Garbage Collector in All Environments
  • JEP 527: Post-Quantum Hybrid Key Exchange for TLS 1.3
  • JEP 534: Compact Object Headers by Default
  • JEP 536: JFR In-Process Data Redaction

Preview JEPとIncubator JEPは5つ。

  • JEP 531: Lazy Constants (Third Preview)
  • JEP 532: Primitive Types in Patterns, instanceof, and switch (Fifth Preview)
  • JEP 533: Structured Concurrency (Seventh Preview)
  • JEP 537: Vector API (Twelfth Incubator)
  • JEP 538: PEM Encodings of Cryptographic Objects (Third Preview)

Java 26のJEPが10でしたから、さらに減っています。

Standard JEPでは、JEP 523とJEP 534がヒープに関するもの。どちらもデフォルト動作が変更されるというものです。

JEP 527とJEP 536はセキュリティ関連ですね。

 

Preview JEPとIncubator JEPは新規はなし。一番少ないものでも3回目のPreviewなので、新規性はないです。

JEPについての詳細は別エントリーで。

 

さて、JEPで語れない方です。Java 27はAPIの変更も少なめ。ほとんどがjava.baseモジュールなので、今回はjava.baseモジュールだけです。

なお、今回もセキュリティ関連や、バージョンに関する定数については省略です。

 

廃止になったAPI

Java 27で削除されたAPIは、メソッドが1つだけです。

メソッド

スレッドプールを提供するExecutorServiceインタフェースのコンクリートクラスであるThreadPoolExecutorクラスのメソッドが1つだけ削除されました。

  • java.util.concurrent.ThreadPoolExecutor.finalize()

基本的にfinalize()メソッドは使わないようにすべきです。ThreadPoolExecutorクラスが実装しているExecutorServiceインタフェースがAutoClosableインタフェースのサブインタフェースになっているため、なおさらfinalize()メソッドは必要ないですね。

もし、finalize()メソッドを使用しているようであれば、try-with-resources構文を使用するようにしましょう。

 

 

廃止予定に追加されたAPI

Java 27で、forRemoval=trueになったAPIはありません。

 

追加されたAPI

Java 27ではStandard JEPでのAPI変更がないので、全体的にAPIの変更も少ないです。

 

java.base/java.langパッケージ

数値演算のメソッドが追加されているのがメインですね。

 

Math/StrickMathクラス

逆双曲線関数関連のメソッドが3つ追加されました。

  • double acosh(double x)
  • double asinh(double x)
  • double atanh(double x)

メソッド名の通り逆双曲線余弦関数、逆双曲線正弦関数、逆双曲線正接関数を求めるためのメソッドです。

 

Stringクラス

文字列をエンコードした時の文字列サイズを求めるメソッドが追加されました。

  • int encodedLength(Charset cs)

Javaは内部文字セットとしてUTF-16を使用しているので、String.length()メソッドではUTF-16のサイズを返します(文字列長ではないです)。これに対し、指定した文字セットでエンコードしたバイト列のバイト長を返すのがencodedLength()メソッドです。

 

jshell>  var text = "こんにちは"
text ==> "こんにちは"

jshell> text.length()
$2 ==> 5

jshell> text.encodedLength(Charset.forName("UTF-8"))
$3 ==> 15

jshell> text.encodedLength(Charset.forName("Shift_JIS"))
$4 ==> 10

jshell> 

内部的にはgetBytes(cs).lengthをコールしているのと同等になります。

 

java.base/java.lang.classfile.instructionパッケージ

Classfile APIのオペコードに対応するインタフェースにメソッドが追加されました。

 

IncrementInstructionインタフェース

IncrementInstructionインタフェースは、オペコードのiincとiinc_wに対応するインタフェースです。

これまでもファクトリーメソッドであるof()メソッドが提供されていましたが、of()メソッドがオーバーロードされました。

  • static IncrementInstruction of(Opcode op, int slot, int constant)

これまでのof()メソッドはオペコードを指定することができませんでしたが、オーバーロードされたof()メソッドはオペコードが指定できます。

指定できるオペコードはOpcode.IINCもしくはOpcode.IINC__Wです。

 

java.base/java.lang.foreignパッケージ

FFMのメモリに関するインタフェースにメソッドが追加されました。

 

MemorySegmentインタフェース

FFMでメモリ領域に対応するMemorySegmentインタフェースにメソッドが2つ追加されました。

どちらも既存のメソッドのオーバーロードになっています。

 

  • static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars)
  • String getString(long offset, Charset charset, long byteLength)

既存のcopy()メソッドは、MemorySegmentからMemorySegmentへのコピーでした。これに対し、新しいcopy()メソッドは文字列をMmoerySegmentにコピーします。

一方のgetString()メソッドはバイト長を指定できるようになりました。

 

SegmentAllocatorインタフェース

メモリーのアロケートを行ってMemorySegmentオブジェクトを返すのがSegmentAllocatorインタフェースです。

こちらも、メソッドのオーバーロードが追加されました。

 

  • MemorySegment allocateFrom(String str, Charset charset, int srcIndex, int numChars)

ソースとなる文字列の開始インデックスと文字数を指定できるようになりました。

 

java.base/java.mathパッケージ

BigDecimalクラスにメソッドが1つ追加されました。

 

BigDecimalクラス

意外にもn乗根を求めるメソッドが今までなかったのでした。BigIntegerクラスはJava 26でn乗根を求めるメソッドが追加されたので、それに続く変更になります。

 

  • BigDecimal rootn(int n, MathContext mc)

第2引数のMthContextクラスは精度を指定するためのクラスです。

 

java.base/java.time.formatパッケージ

Date & Time APIのフォーマッターに関するクラスが新規で導入されました。

 

DateTimeFormatterPatternProviderクラス

DateTimeFormatterPatternProviderクラスロケールごとに日時フォーマットを文字列を作成するためのクラスで、LocaleServiceProviderクラスのサブクラスです。

DateTimeFormatterPatternProviderクラスも抽象クラスであり、アプリケーションごとに以下のメソッドをオーバーライドします。

 

  • String getDateTimeFormatterPattern(String requestedTemplate, String calType, Locale locale)
  • String getDateTimeFormatterPattern(FormatStyle dateStyle, FormatStyle timeStyle, String calType, Locale locale)

 

その他の変更

Java 27というわけではありませんが、2026年8月からOracleがセキュリティアップデートの変更を行いました。

今までのセキュリティアップデートはCPU (Critical Patch Update)といい、1月、4月、7月、10月に行われてきました。

これに対し、新たにCSPU (Critical Security Patch Update)が追加され、2月、3月、5月、6月、8月、9月、11月、12月に行われます。

CPUとCSPUを合わせると、毎月セキュリティアップデートがあることになります。

すでに、2026年8月にはCSPUに対応するJava 26.0.2.1がリリースされています。今までのCPUが3つ目の数字で、CSPUが4つ目の数字ということになったようです。

これはOracle JDKの話ですが、結局は他のディストリビューターも追従するようですね。

 

参考: 月次のCritical Security Patch Updates (CSPUs)を2026年5月28日より開始

 

 

まとめ

LTSの間の中間のバージョンだからなのか、変更点がとても少ないです。

JEPによる変更も少ないのですが、これに関しては次のエントリーで紹介します。

 

次のJava 28は、やっとValue ClassがPreviewになります。これ以外にもプリミティブ型のパターンマッチングなどがStandard JEPになりそうなので、いろいろ動きがありそうです。

Java 27は嵐の前の静けさなのかもしれません。

2026/08/19

探偵!ナイトスクープ Java Ring回の裏側

このエントリーをはてなブックマークに追加

ひょんなことから探偵!ナイトスクープに出演することなっていました。人生長く生きていると、何が起こるか分からないものです。

 

どうして出演することになったのかとか、他の出演者がどうやって決まったのかなどの話はともかく、Java Ringですよ。

実際の撮影は1日がかりだったのですが、放送はたった10分。削られた部分が多くあるので、その部分を補っていくというのが、このエントリーです。

 

依頼者の山﨑さんも顛末をブログに書いているので、そちらもぜひご覧になってください。

 

番組はYouTubeで見られます。

 

Java Ring

Java Ringは1998年にサンフランシスコで開催されたJavaOne 98で、Sun Microsystemsが参加者に配布したガジェットです。その後、日本など世界各地のJavaのカンファレンスやイベントで参加者に配布されています。

櫻庭はJavaOne 98でもらったもの以外に、日本の2つのイベントでも取得したので3つあったのですが、1つは行方不明😰

イベントで配布されたものはプロトタイプで、その後実際に販売されたようですが、製品版は見たことがないです。

 

Java Card

さて、この当時、Javaに複数のエディションを作成すると発表されていました。Java SE (Standard Edition)、Java EE (Enterprise Edition)、Java ME (Micro Edition)の3種類です。今もOpenJDKで開発が進められているのが、Java SEですね(SEとは言わなくなってしまいましたが...)。そして、Jakarta EEがJava EEの後継です。

残念ながら組み込み向けのJava MEはなくなってしまいましたが、Java MEよりもさらにリソースが限られたデバイス向けのJavaがあります。

それがJava Cardです。

Cardという名前であることから分かるとは思いますが、クレジットカードがメインのターゲットです。その他にもSIMカードなどにJava Cardは使われていました。

クレジットカードを見れば分かる通り、バッテリーはありません。つまり、カードを親機に接続したときに供給される電力だけで動作します。

また、計算リソースも極端に限定されており、浮動小数点数はおろか、整数でも4バイト幅のintは使用できません。

このため、Javaといっても、Java SEとはかなり違うものです。

 

Java CardではCardletというアプリケーションが動作します。このCardletのクラス名がAppletなのが誤解を生んでいました。

Java CardのAppletはjavacard.framework.Appletクラスで、いわゆるアプレットはjava.applet.Appletクラス。できることも違えば、メソッドも異なりますし、動作も全然違います。

Cardletでは、ほんとに限定的なことしかできないのです。まぁ、クレジットカード上で動作すると考えれば妥当ですね。

 

Java Ringとは

さて、Java Ringです。

Java Ringは、Java Cardに準拠したデバイスです。

もともとはiButtonというボタン型のデバイスがありました。そこにJava CardのVMであるCardVMをのせ、指輪状に加工したのがJava Ringです。

通常のJava Cardとは違って、バッテリーが内蔵されており、メモリもクレジットカードに比較すれば多く使用できました。このため、複数のCardletを使用できるようになっています。ただし、複数のCardletを同時に動かすことはできません。

1998年のJavaOneでは、山﨑さんがナイトスクープで言及してたコーヒーの好みを記憶させておくCardletや、マンデルブロー集合の各ピクセルを計算させてJavaOne参加者でマンデルブロー集合の図を表示させるCardletが使用できるようになっていました。とはいえ、コーヒーの好みを記憶させておくだけで、Java Ringを接続するとコーヒーを淹れてくれるというのはなかったんですけどね。

 

このようなJava Ringですが、バッテリーを搭載しているために、逆にバッテリーがなくなってしまうと動作しなくなってしまいます。そこで、山﨑さんがバッテリーを交換したいとナイトスクープに駆け込んだわけです。

 

Java Ringの構造

ソフトウェアの構造でもハードウェアでもなく、物理的な構造です。

Java Ringは、iButtonを指輪状に加工したものです。ベースになるのはiButtonが収まるサイズの穴がある指輪です。

その穴に樹脂を流し込んで、その後にiButtonを埋め込み、さらに隙間に樹脂を流し込んで、iButtonを固定してあります。

Java Ringの穴の裏側には空気抜きのための小さな穴が開いています。

_DSC5217

iButtonは筐体全体が接点になっており、側面および底面がグランド、トップの部分が信号ラインとなっています。これをアダプターに接続して親機と通信できるようになっています(この通信方法は1-Wireと呼ばれています)。

このため、Java Ringでもアダプターに接続できるように、トップの部分が露出した形状になっています。

_DSC5233

 

さて、このような構造のJava Ringですが、バッテリーを交換するにはiButtonを開封しなければいけません。

ナイトスクープではJavaの専門家たちに集まっていただきましたが、さすがにソフト屋には手も足も出ません。

そこで、登場していただいたのがプロモデラーのまつおーじさんです。

今回、Java Ringが復活できたのも、ほんとにまつおーじさんのおかげです。

まつおーじさんが行ったのが、iButtonの周りの樹脂の部分をタガネで掘っていくというものです。下の図にJava Ringの輪切りにした図ですが、この黒い樹脂の部分を削っていったわけです。

番組ではあっという間に掘り終わったように見えますが、実際にはこれだけで2時間近くかかっています。

上から金属が見えるところまで掘り進み、その後裏側の小さい穴も掘り進めていきます。これで下図のような状態になりました。

この後、小さい万力を利用して、下の穴から力をかけて押していきました(上手の矢印の部分)。この結果、iButtonがスポッと取り出せたのでした。

これも番組では簡単に取り出せたように見えますけど、これもなかなか引っかかりがなくて時間がかかっています。

次は、取り出したiButtonを切断してバッテリーを取り出すわけですが、これもそんなに簡単にできるものではありません。

もともとiButtonはバッテリーを交換するようにはできていません。バッテリーを取り出すには、何らかの方法でiButtonを切断しなくてはなりません。

山﨑さんも紹介しているiButtonの電池交換レポートでは、グラインダーを使って切り進んでいますが、これは中の基盤などを壊してしまう可能性も高いです。

iButtonの形状を図式化したのが下図です。

ここで、まつおーじさんが注目したのが、iButtonの底部にあるフリンジです。下の図はiButtonを水平に見た時の図ですが、フリンジは赤丸の部分ですね。

このフリンジは、iButtonを他のデバイスなどに装着しやすくするための出っ張りです。ですが、iButton自体をかしめるにもこの形状が都合がよかったのだと思います。

このフリンジの部分をペンチで繰り返し曲げ伸ばしすることで、金属疲労によりちぎりとっていったわけです。

最終的には缶詰を開けた時のように、一周ぐるりとフリンジを取り除き、底部をパカッと開けることができたのでした。

そして、バッテリーは底部にあったので、無事に交換ができたのでした!!

ちなみに、下の写真はバッテリーを抜いた後です。

と、ここまでが物理的な話。番組だとかなりはしょられてはいましたけど、こちらがメインでしたね。

まぁ、ソフトは何かやっていても絵にならないからしかたないです。

 

Java Ringのソフトウェア構成

さて、ここからがソフトウェアの話です。

Java Ringのソフトウェアの構成を示したのが下の図です。

ベースの部分にファームウェアがあり、その上にJava CardのVMであるCardVMが動作しています。リソースが限られているため、OSに相当する部分はありません。

また、ファームはバッテリーがなくても、1-Wireからの給電で最低限の動作は可能です。

山﨑さんのblogエントリーの 【Java Ring】05. CRC エラー! で、バッテリーがなくても通信ができているのはこのためです。しかし、バッテリーが切れてしまったため、CardVMは動作しません。このため、CRCエラーが発生してしまっています。

そして、CardVM上でCardletが動作するという構成になっています。

また、Master PINなどの秘密情報を保持している領域も用意されています。

 

このような構成になっているわけですが、当然のことながらそれぞれの構成要素がメモリーのどこかに保持されています。

問題はどこまでがバッテリーがなくてもメモリー内容を保持できるかどうかということです。

メモリーには電源が供給されないと内容を保持できない揮発性メモリーと、バッテリーがなくても大丈夫な不揮発性メモリーがあります。

不揮発性メモリーにも、長期的に安定しているものと、長期的には内容を保持できないものがあります。

前者にはEPROM、後者にはフラッシュメモリーなどがあります。前者は一般的にROM、後者はNVRAMと呼ばれることがあります。

iButtonでも、EPROMとNVRAMが使われています。

問題はどこまでがROMに保存されているかということです。

もう1つ考えなくてはならないのが、耐タンパー性です。

耐タンパー性とは外部からの攻撃を受けた時の耐性を示しています。攻撃に対する対応にはいろいろありますが、漏洩してはならない情報を消去するという手法もあります。

Java Ringはパーソナルなガジェットなので、分解しようとすると暗号鍵などの秘密情報は耐タンパー性により消去されてしまうのではないかということです。

事前に大山さん、石原さんと話していたのは秘密情報は、まず消去されるでしょうということ。また、Cardletもバッテリーがなくなれば保持できないであろうということです。

で、議論になったのが、CardVMです。

製品版のJava Ringならともかく、プロトタイプでイベント用に配布するために作られたJava RingでCardVMをわざわざROMに焼くかということです。

3人の意見は、多分ダメだろうと。

ところが、バッテリーを交換してビックリ、CardVMが生きていたのでした!!

 

とはいえ、秘密情報は消去されてしまったので、初期化されてしまった状態です。収録の時には、Cardletをロードするところでエラーになってしまいました。事前に工場出荷時のMaster PINの候補はいくつか探していたのですが、いずれも失敗。

結局、残念ながら時間切れ。

その後、山﨑さんが自宅で設定をいろいろと見直すことでCardletのロードに成功したのでした。どうやら、工場出荷時にはMaster PINが設定されていなかったようです。

その後、Cardletの実行までできたのは彼のblogにある通りです。

 

ということで、Java Ringが復活したのでした!!

2026/04/06

JEPで語るJava 26

このエントリーをはてなブックマークに追加

gihyo.jpの記事や、JavaOneの参加レポート(前編後編)などを書いていたら、JEPで語るが4月になってしまいました😰

Java 26のJEPは10。そのうちの半分がStandard JEPです。LTSの次のバージョンにしては多いですね。

Java 26のJEPの一覧はこちら。

  • 500: Prepare to Make Final Mean Final
  • 504: Remove the Applet API
  • 516: Ahead-of-Time Object Caching with Any GC
  • 517: HTTP/3 for the HTTP Client API
  • 522: G1 GC: Improve Throughput by Reducing Synchronization
  • 524: PEM Encodings of Cryptographic Objects (Second Preview)
  • 525: Structured Concurrency (Sixth Preview)
  • 526: Lazy Constants (Second Preview)
  • 529: Vector API (Eleventh Incubator)
  • 530: Primitive Types in Patterns, instanceof, and switch (Fourth Preview)

 

では、1つずつ紹介していきましょう。

 

JEP 504: Remove the Applet API

JEPのタイトル通り、Appletに関するAPIを削除するJEPです。

Applet Viewerなどのアプレットを動作させる方はとっくに削除されていましたが、APIはまだ残っていたのでした。

java.appletパッケージが削除されただけではなく、SwingのJAppletクラスも削除されています。

実際にはAWTやJava 2Dの内部でアプレットに関連した部分がいろいろと削除されているようです。

 

JEP 500: Prepare to Make Final Mean Final

finalをfinalにしましょうというJEPです。

「どういうこと?」と思われるかもしれませんが、今はfinalは実はfinalではないのです。

たとえば、次のプログラムを実行してみるとどうなるでしょう。

class Foo {
    final int x;

    public Foo() {
        x = 0;
    }
}

void main() throws Exception {
    var foo = new Foo();
    IO.println("Initla Value: " + foo.x);

    var clazz = Foo.class;
    var refX = clazz.getDeclaredField("x");

    // おまじない
    refX.setAccessible(true);
    
    // 値の変更
    refX.set(foo, 10);
    IO.println("Modified Value: " + foo.x);
}

リフレクションでFooクラスのxフィールドの値を書き換えているプログラムです。

このプログラムをJava 25で実行してみましょう。

C:\src> C:\Program Files\Java\jdk-25\bin\java -version
openjdk version "25" 2025-09-16
OpenJDK Runtime Environment (build 25+36-3489)
OpenJDK 64-Bit Server VM (build 25+36-3489, mixed mode, sharing)

C:\src> C:\Program Files\Java\jdk-25\bin\java Main.java
Initla Value: 0
Modified Value: 10

C:\src>

あっさりと値の書き換えができてしまいました。

ここでキーになるのはsetAccessible()メソッドです。setAccessible()メソッドの引数にtrueを指定してコールすると、finalフィールドへの代入、privateメソッドのコール、privateフィールドの柿替えなどができてしまいます。

つまり、finalフィールドであってもfinalではないという状況を作り出してしまったわけです。

 

このようにfinalフィールドの書き換えができるということをやめましょうというのが、JEP 500です。

ただし、いきなり禁止にしてしまうと、動作できなくなるアプリケーションも出てくるので、まずは警告を出すようにして、将来的には原則禁止にしていきましょうということです。

 

では、同じプログラムをJava 26で実行してみましょう。

C:\src> C:\Program Files\Java\jdk-26\bin\java -version
openjdk version "26" 2026-03-17
OpenJDK Runtime Environment (build 26+35-2893)
OpenJDK 64-Bit Server VM (build 26+35-2893, mixed mode, sharing)

C:\src> C:\Program Files\Java\jdk-26\bin\java Main.java
Initla Value: 0
WARNING: Final field x in class Main$Foo has been mutated reflectively by class Main in unnamed module @4c6e276e (file:/C:/Main.java)
WARNING: Use --enable-final-field-mutation=ALL-UNNAMED to avoid a warning
WARNING: Mutating final fields will be blocked in a future release unless final field mutation is enabled    
Modified Value: 10

C:\src>

値の書き換えはできるものの、Warningが出るようになりました。これがJEP 500によるものです。

 

さて、リフレクションによるfinalフィールドの値書き換えができなくなるのはいいのですが、ライブラリやフレームワークがリフレクションを使っていて警告が出てしまう場合はどうすればよいでしょう?

対応策は2種類あります。どちらも実行時オプションで指定します。

  • モジュール単位でリフレクションによるfinal変更を許可する
  • 全体の動作を指定する

1つめのモジュール単位で許可する方法は、上記のWarningの文章の中にもある--enable-final-field-mutationオプションです。

--enable-final-field-mutation=m1,m2 のようにの後に許可するモジュールをカンマ区切りで列挙します(m1とm2がモジュールです)。

すべてに許可する場合は --enable-final-field-mutation=ALL-UNNAMED とします。

 

もう一方の全体の動作を指定するには、--illegal-final-field-mutationオプションで行います。

指定できるのは、以下の4種類です。

  • allow : finalの書き換えを許可
  • warn : finalの書き換えがあると警告を出力 (デフォルト)
  • debug : finalの書き換えがあると警告とスタックトレースを出力
  • deny : finalの書き換えを禁止

Java 26でのデフォルトはwarnです。しかし、将来的にはdenyがデフォルトになる予定です。

denyがデフォルトになる前に、警告が出たら対応しておきましょう。

 

JEP 517: HTTP/3 for the HTTP Client API

java.net.httpモジュールで提供されているHTTP ClientをHTTP/3に対応させるというJEP。

HTTP/3はTCPではなく、UDPで通信するのですが、ブラウザーで使っていると違いは全然分からないですね。同じようにHTTP Clientでも、既存の使い方とまったく変わらずに通信することができます。

違いは、HTTPのバージョン指定でHTTP/3にすることだけです。

このために、HTTPのバージョンを表すHttpClient.Version列挙型に定数が追加されています。

  • HTTP_3

後は、HttpClientオブジェクトを生成するときにバージョンを指定するだけです。もしくは、リクエストごとに指定することも可能で、その場合はHttpRequestオブジェクトを生成するときにバージョンを指定します。

前者であれば、次のように記述します。

    var client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_3)
            .build();

後者のリクエストで指定するには、次のようになります。

    var request = HttpRequest.newBuilder()
            .uri(URI.create("https://google.com/"))
            .version(HttpClient.Version.HTTP_3)
            .GET()
            .build();

後は、通常の使い方と同じです。

ただし、実際にHTTP/3が使われるかどうかは、Webサーバーとのネゴシエーションによるので、HTTT/3を指定したけどHTTP/2で通信していたということもあります。

 

ただ、動作がよく分からないこともあります。たとえば、google.comに接続するときにHttpClientオブジェクトでバージョン指定してもHTTP/2が使われます。しかし、HttpRequestオブジェクトにバージョン指定するとHTTP/3で通信できます。

何かが違うのでしょうけど、イマイチよくわからず...

 

JEP 516: Ahead-of-Time Object Caching with Any GC

Standard JEPの残り2つはGC関連。ただし、JEP 516はGCというよりは、Project Leydenです。

起動時間やウォームアップ時間を短縮するために、Project LeydenではAOTキャッシュファイルを使用します。

AOTキャッシュにはロードしたクラスや、HotSpotVMのプロファイリング解析結果などを保存しておきます。

また、AOTキャッシュでは初期化したオブジェクトも保存して置くことが可能です。ここで、GCとのやり取りが出てくるわけです。

JEP 516はタイトルにAny GCとありますが、実をいうとすでにG1 GCやParallel GC、Serial GCはAOTキャッシュに対応済みでした。

しかし、ZGCは対応ができていませんでした。そこで、GCに依存しような形式でオブジェクトをキャッシュできるようにしましょうというのがJEP 516です。ただし、GCに非依存にするため、効率は若干落ちてしまいます。

このため、今まで対応できていなかったZGCやShenandoah GCなどを使用する時だけ、GC非依存フォーマットにする方がよさげです。

GCに非依存のオブジェクトキャッシュを使用する場合は、AOTキャッシュを作成する時に-XX:+AOTStreamableObjectsオプションを指定します。

 

JEP 522: G1 GC: Improve Throughput by Reducing Synchronization

G1 GCではGCのたびにオブジェクトが領域を移動し、このためオブジェクト参照先のオブジェクトのアドレスが変更されます。この参照をたどることを効率的に行うためにカードテーブルというテーブルを使用します。

このカードテーブルはGCのスレッドだけでなく、アプリケーションスレッドでも使用します。

また、G1 GCではレイテンシーを改善するために、GCスレッドとアプリケーションスレッドが並列に動作する時間が長くなります。

つまり、GCスレッドとアプリケーションスレッドの両方からアクセスされるカードテーブルは、アクセスする時に同期化が必要になるということです。

この同期化によってパフォーマンスが低下してしまうことありました。それを改善しましょうというのが、JEP 522です。

 

では、どうやって解決するかというと、カードテーブルを2つ用意して、一方を読み込み、他方を書き込み専用にしてしまおうという手法。まぁ、CopyOnWriteArrayListクラスのようなものです。

とはいうものの、従来の手法でもロック取得待ちが発生することはそれほどないはずです。よっぽどヒープが逼迫して、頻繁にGCが発生するような場合でもなければ、JEP 522の恩恵に預かることはないかもしれません。

 

Preview/Incubator JEP

ここからはお試し機能のPreview/Incubator JEPなので、簡単に触れるだけにしましょう。

いずれも、まだ変更が入る可能性があるので、その点はご注意ください。

 

JEP 524: PEM Encodings of Cryptographic Objects (Second Preview)

PEMエンコードは証明書や鍵交換で使用されるバイナリをBase64でテキスト化するエンコードです。

Java 26ではPEMRecordクラスがPEMクラスに変更されたなどがあります。

そして、次のPEMエンコードのJEPでStandard JEPになるようです。今のところ、ターゲットとなるバージョンが確定していないのですが、Java 27で正式になるでしょう。

 

JEP 525: Structured Concurrency (Sixth Preview)

6回目のプレビューでなかなかStandard JEPにならないStructured Concurrencyです。

複数のタスクをパラレルに処理させて、その結果をまとめるという用途で使用します。

Java 26ではタイムアウトを設定できるようになったり、結果をストリームで返していたのを、リストにするなどの変更がありました。

次のJEPもドラフトになっているのですが、まだプレビューのままのようです。

 

JEP 526: Lazy Constants (Second Preview)

Lazy Constantsは、Java 25ではStable ValuesだったAPIです。

Lazy Constantsについては、JJUG CCC 2025 Fallでプレゼンしましたし、解説ブログも書きましたので、そちらをご参照ください。

 

 

JEP 530: Primitive Types in Patterns, instanceof, and switch (Fourth Preview)

intやdoubleなどのプリミティブ型をパターンマッチングで使用できるようにしようというのがJEP 530です。

こちらもなかなかStandard JEPにならないですねぇ。

プリミティブ型は暗黙の型変換が絡むので、そんなこともなかなかStandard JEPにならない要因になっている気がします。

実際にJava 26でも型変換に関して厳密になるような変更が加えられています。

そして、次のJEPもまたPreview JEPになっています。ただし、次のJEPでは変更がないので、このまま行けばJava 28でStandard JEPになりそうです。

 

JEP 529: Vector API (Eleventh Incubator)

Value Classが導入されるまでIncubator JEPのままが確定しているVector APIですが、Java 26でも変更はありません。

このままずっと塩漬け状態が続くのかと思っていたのですが、内部的にはいろいろと変更があるようです。というのもJavaOneでVector APIのセッションがあったのですが、そこでどういうことを行っているかについて説明があったからです。

資料は公開されているので、参考までに。

 

まとめ

Java 26の10のJEPについて簡単に解説しました。

Standard JEPでAPIが変更されるのはJEP 517だけですが、他のJEPは安全性やパフォーマンスの強化が図られています。

だからといって、Java 26をインストールしましょうというほどではないですね。次のLTSまでの間にこのような強化が積み重なっていけばよいと思います。

次のJava 27はまだJEPが1つだけですが、ドラフトにいろいろ上がってきているのでどれがJava 27をターゲットにするのか楽しみですね。