HeadtowardJava15andJava16
KUBOTAYuji
LINECorporation
JJUGCCC2020Fall(2020/Nov/7)
KUBOTAYuji(@sugarlife)
SoftwareEngineeroninternalKafkaplatformteam@LINE
OpenJDKAuthor/IcedTeacommitter
JavaOne2014/2016/2017,WEB+DB「DivetoJava」(2018-19)
https://www.slideshare.net/YujiKubota/
2
今⽇話すこと
Java15/16の新機能と変更点
Java16の情報は10/10時点のもの
話さないこと
⾮互換性
APIの細かい変更点
http://cr.openjdk.java.net/~iris/se/15/latestSpec/apidiffs/overview-
summary.html
3
References
bugs.openjdk.java.net(通称b.o.j.n)
JEPsforJava15,CSRsforJava15
JEPsforJava16,CSRsforJava16
https://github.com/openjdk/jdk
jdk-updates/jdk15u (mercurial)
ThereisalotofinformationonthelatestJDKavailableinJapanese.
Welcome,Java15!by@logico_jp
Java15新機能まとめby@kis
JEPでは語れないJavaSE15by@skrb
HeadtowardJava14andJava15byme
4
Glossary:⽤語集(1/2)
JEP(JDKEnhancement-Proposal)
JEP1で定義されているJava新機能拡張の提案
CSR(Compatibility&SpecificationReviews)
⾮互換性を伴う変更のレビュー(⾮互換性を伴う変更は出す必要がある)
Releasenotes
各製品ごとの変更点リスト。製品ごとに違うので確認しましょう
OpenJDK:b.o.j.n
OracleJDK:https://www.oracle.com/java/technologies/javase/jdk-relnotes-
index.html
これらを確認すれば⾮互換性を含む変更点は網羅的に確認できる?
5
Additionalincompabilitiyexample
No. #now() returnsnanosecondspartbyJDK-8068730(Java9)
# Version 14.0.2
jshell> var now = java.time.LocalDateTime.now()
now ==> 2020-10-08T16:19:01.738366
# Version 15
jshell> var now = java.time.LocalDateTime.now()
now ==> 2020-10-08T16:19:08.485590579
jshell> var now = java.time.LocalDateTime.now()
...> .truncatedTo(java.time.temporal.ChronoUnit.MICROS)
now ==> 2020-10-08T16:19:12.943847
6
Additionalincompabilitiyexample(Cont.)
#now() returnsnanosecondspartbyJDK-8068730(Java9)
# Version 15, Linux
jshell> var now = java.time.LocalDateTime.now()
now ==> 2020-10-08T16:30:29.534948147
jshell> System.getProperty("os.name")
$2 ==> "Linux"
# Version 15, macOS
jshell> var now = java.time.LocalDateTime.now()
now ==> 2020-10-08T16:31:26.815258
jshell> System.getProperty("os.name")
$2 ==> "Mac OS X"
Windowsはもっと前(環境がないので未確認)
7
Glossary:⽤語集(2/2)
Incubator(JEP11)
JavaAPIの試験⽤モジュール( jdk.incubator )
標準化に向けてフィードバックを得て変更しやすいように特別なモジュールにしている。有
効にするには --add-modules jdk.incubator.xxx の指定が必要
Preview(JEP12)
Java⾔語やJVMの試験機能。有効にするには --enable-preview の指定が必要。コンパイ
ル時には --resource か -source の指定も必要
Standard
上記のテストフェーズを通じて標準機能に昇格した機能。今のところは2回(例:Preview->
SecondPreview->Standard)で昇格している
8
Java15
14JEPs
JEPsforJava15
127CSRs
CSRsforJava15
Java16(Oct.10)
8JEPs
JEPsforJava16
91CSRs
CSRsforJava16
9
Language/APIchanges
Java15
360:SealedClasses(Preview)
371:HiddenClasses
373:ReimplementtheLegacyDatagramSocketAPI
375:PatternMatchingforinstanceof(SecondPreview)
378:TextBlocks
383:Foreign-MemoryAccessAPI(SecondIncubator)
384:Records(SecondPreview)
385:DeprecateRMIActivationforRemoval
Java16
338:VectorAPI(Incubator) 10
15:375:PatternMatchingforinstanceof(SecondPreview)
背景: instanceof で型⽐較して⾃明でもキャスティング等が必要
利点:コードの可視性・メンテナンス性が向上
前回(JEP305)から変更なしでSecondPreviewへ
// Before
if (obj instanceof Double) {
double d = ((Double) obj).doubleValue();
// uses d here.
}
// After
if (obj instanceof Double d) {
// uses d here.
// bindingしてる値に代⼊はダメ、コンパイルエラーが発⽣する
d = 2.0;
} else {
// can't use d here.
}
11
(Drafting)thesamewayon switch
別JEPでswitch⽂でも利⽤できるようになる予定です。ただし時期未詳
switch (obj) {
case Integer i:
// uses i here.
case Double d:
// uses d here.
}
public double area(Shape shape) {
return switch(shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.length() * r.width();
case Square s -> s.length() * s.width();
default -> 0;
}
}
12
15:378:TextBlocks
背景:Javaにもヒアドキュメント表記機能を!
利点: String にヒアドキュメント機能が追加された
前回(JEP-368)から変更なしで標準機能に昇格
jshell> var textblock = """
...> Duke is 
...> cute s
...> """;
textblock ==> "Duke is cute n"
13
15:384:Records(SecondPreview)
背景:lombokの@Valueのように安全なデータクラスを作るために考慮・実装しなければいけ
ない範囲が多すぎる
利点:標準APIで安全(でシンプル)なデータモデルを作れる
前回からローカルクラス・インターフェースへの追加対応が⾏われた
14
15:384:Records(SecondPreview)(Cont.)
//⼀番シンプルな書き⽅
public record TestRecord(String s, int i) {}
public final class TestRecord(String s, int i)
extends java.lang.Record {
private final String s;
private final int i;
String s() { return s; }
int i() {return i;}
TestRecord(String s, int i) {
this.s = s;
this.i = i;
}
// toString(), equals(), hashCode()
}
15
15:384:Records(SecondPreview)(Cont.)
public final class TestRecord(String s, int i)
extends java.lang.Record {
Immutable
recordは常に final クラス
(java.lang.Recordを拡張しているので)recordはインターフェースを実装できるがクラ
スを拡張はできない
(同じ理由で)Objectクラスが持つ以下の3つのメソッドが暗黙的に実装されている
// toString(), equals(), hashCode()
equals() はrecordが持つフィールドが同値であれば常に true
16
15:384:Records(SecondPreview)(Cont.)
private final String s;
private final int i;
Immutable
フィールドは private final でsetterもなし
Reflectionを使っても変更は不可
やったら IllegalAccessException が投げられる
String s() { return s; }
int i() {return i;}
FluentgetterAPI, getS や getI のように getXX は追加できない
17
15:384:Records(SecondPreview)(Cont.)
// ⼀般的な⽣成されたコンストラクタ
TestRecord(String s, int i) {
this.s = s;
this.i = i;
}
下記のようにコンストラクタで検証(validate)や値の⽣成も可能
public record TestRecord(String s, int i) {
public TestRecord() {
this("Default S", 1000);
}
public TestRecord(String s, int i) {
if (s.isEmpty()) {
throw new IllegalArgumentException();
}
this.s = s;
this.i = i;
}
}
18
15:360:SealedClasses(Preview)
背景:実装・継承先を同⼀コンパイル単位に限定したい
利点:実装を⾏うコードや(将来的に)パターンマッチの範囲を指定できる
// classの前に sealed を宣⾔
public sealed class Shape
// permits の後に実装・継承先を記述する
permits com.example.polar.Circle,
com.example.quad.Rectangle,
com.example.quad.simple.Square {...}
// interfaceも同様
public sealed interface Shape
permits Circle, Rectangle, Square {...}
19
15:360:SealedClasses(Preview)(Cont.)
permits で指定したサブクラスにおける制約
sealedクラス/インターフェースと同⼀モジュール内
unnamedmoduleの場合は同⼀パッケージ内
直接sealedクラス/インターフェースを拡張する必要がある
以下の3つのconstraintsのうち⼀つを明記する必要がある
final :これ以上の拡張を禁⽌
sealed : permits で指定したサブクラスのみ許可
non-sealed :制約なし
public final class Rectangle extends Shape {...}
public sealed class Rectangle extends Shape
permits Cube {...}
public non-sealed class Square extends Shape {...}
20
15:360:SealedClasses(Preview)(Cont.)
パターンマッチ(型テストパターン)の範囲を指定できる...とは?
sealedクラスを引継ぐサブクラスが指定されている
コンパイラ(javac)が網羅的な範囲がわかる
default が不要になる
網羅されてなければコンパイル時に記載を推奨される
// p.12 より再掲
public double area(Shape shape) {
return switch(shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.length() * r.width();
case Square s - s.length() * s.width();
}
}
Recordと組み合わせることも可能
Recordは常に final なのでconstraintsを気にする必要もない 21
15:371:HiddenClasses
秘匿クラスの導⼊
背景:
.現在の実装は動的クラスか静的クラスかが区別できず、無差別にアクセスできてしま
い、主にフレームワーク開発者の意図から離れて⻑命オブジェクトとなる場合がある
. sun.misc.Unsafe::defineAnonymousClass の⾮推奨化
利点:限定された⽅法以外では他クラスやインターフェースからはアクセスできない秘匿クラ
スを作るAPIが提供され、他クラスから独⽴してアンロード可能になる
静的クラス:あるクラスのバイトコードがコンパイル時に⽣成されたもの
動的クラス:ランタイム時に作成されたもの
よくある⻑命化するパターン:静的クラスから動的クラスにリンクしてしまい、静的クラス
が開放されるまで動的クラスが保持され続ける
22
15:371:HiddenClasses(Cont.)
全クラスは同じクラスローダー階層にいる別クラスから名前解決して
リンクすることが可能
Class::forName , Classloader::loadClass
Hiddenclassを作るためのAPIがMethodHandles.Lookupに追加された
MethodHandles.Lookup#defineHiddenClass(byte[] bytes, boolean
initialize, MethodHandles.Lookup.ClassOption... options)
byte[] にクラス情報を格納してわたし、reflectiveaccessを提供する Lookup オ
ブジェクトを獲得する
この Lookup オブジェクトが唯⼀のアクセス⼿段
MethodHandles.LookupはJava7からあるので具体的な使い⽅はAPIDocを参照
23
15:371:HiddenClasses(Cont.)
$ cat Hidden.java
public class Hidden {
public void hello() {
System.out.println(hello);
}
}
$ cat HiddenCaller.java
import java.lang.invoke.MethodHandles;
import java.nio.file.*;
public class HiddenCaller {
// 簡易化のために例外処理は投げ捨ててます(Don't do that)
public static void main(String... args) throws Exception {
MethodHandles.Lookup lookup = MethodHandles.lookup();
// ローカルファイルシステム上にあるクラスファイルからbyte[]へ格納
byte[] bytes = Files.readAllBytes(Paths.get(Hidden.class));
// 新しく追加されたAPIでHidden classを定義
lookup.defineHiddenClass(bytes, false);
Class? clazz = lookup.findClass(Hidden);
// 従来と同様の⼿順で hello メソッドを発⽕
Object instance = clazz.getDeclaredConstructor().newInstance();
clazz.getDeclaredMethod(hello).invoke(instance);
}
}
$ java HiddenCaller
hello
ref.https://blogs.oracle.com/javamagazine/the-unsafe-class-unsafe-at-any-speed 24
15:383:Foreign-MemoryAccessAPI(SecondIncubator)
背景:セキュアで効率的なヒープ外メモリにアクセスする標準APIがない
利点:既存⼿段より効率的かつメンテナンスされる標準APIが提供される
既存⼿段のデメリット
allocateDirect ( ByteBuffer )
オブジェクトごとに上限2G( Integer.MAX_VALUE )
メモリ解放タイミングがGC依存
JavaNativeInterface(JNI)
メモリ管理を⾃分でやるのが⼿間
ネイティブとJava間のキャスティングがオーバヘッド
Unsafe.allocateMemory()
名前の通りJVMクラッシュを引き起こすなどUnsafe
⾮公式APIなので将来的に無警告で削除の恐れがある
有効にするには --add-modules jdk.incubator.foreign が、必要 25
15:383:Foreign-MemoryAccessAPI(SecondIncubator)(Cont.)
Keyconcepts
Thread:Segmentを作成して所有するスレッド
Segment:メモリセグメント。JavaヒープあるいはJavaヒープ外にあるメモリ領域をモ
デル化した概念。所有しているスレッドのみがアクセスできる(所有権は移譲可能)
Slice:以下の境界(bounds)を共有するSegmentの⼀部領域
空間的境界:SliceはSegmentの空間範囲内に存在する
時間的境界:SegmentとSliceは同時にクローズされる
Segmentが閉じられるとSliceも閉じられる。逆も同様
26
15:383:Foreign-MemoryAccessAPI(SecondIncubator)(Cont.)
今回追加されたMemoryHandlesを⽤いてJava9から導⼊されている
VarHandleを介してメモリセグメントにアクセスする
// VarHandleを介して int 型を準備
VarHandle intHandle =
MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
// 100 bytesメモリセグメントをアロケートしてアクセス
try (MemorySegment segment = MemorySegment.allocateNative(100)) {
MemoryAddress base = segment.baseAddress();
for (int i = 0; i  25; i++) {
// Foreign-Memory Access.
intHandle.set(base.addOffset(i * 4), i);
}
}
ref.http://cr.openjdk.java.net/~mcimadamore/panama/foreign-memaccess.html
27
15:373:ReimplementtheLegacyDatagramSocketAPI
UDPSocketAPI再実装
背景:Java13でTCPSocketAPIの再実装(JEP353)が⾏われたので、追従してUDPSocket
APIもVitrualThreadに対応。旧実装はC⾔語とJava⾔語が共存していたのでメンテナンス性
も悪い状態
利点:アプリケーションを改修せずに勝⼿に⾼速化(参考ブログ)
旧実装を利⽤したい場合は、
-Djdk.net.usePlainDatagramSocketImpl=true
${java.home}/conf/net.properties に設定でも可
28
15:385:DeprecateRMIActivationforRemoval
Java8から必須でなくなったRMIActivationを⾮推奨化して将来的に削除
背景:現在の分散処理環境にはそぐわないアーキテクチャ2003年を最後にStackOverflowで
質問もされてないほど使われてないのに関わらずメンテナンスコスト(特にテスト)はずっとか
かっていた
利点:(ユーザ的には)とくになし
java.rmi.activation パッケージと関連インターフェースやクラスが削除フラグ付き
の⾮推奨化
rmid ツールも警告が出るようになった
RMI⾃体は削除されずこのままメンテナンスされる
29
16:338:VectorAPI(Incubator)
背景:最近のCPUはSIMDアクセラーションを持つAVX2等の⾼度なSIMD演算をサポートして
いるが、これをJavaAPIから利⽤する⼿段がない
Hotspotは⾃動ベクトル化をサポートしているが、変換可能なスカラ演算は限られている等
で、コードに対して表現⼒や柔軟性がない
利点:アーキテクチャに依存せず、SIMDのベクトル演算が標準APIで簡潔かつ明瞭に書ける
サポート外のCPUアーキテクチャでベクトル演算をハードウェアシーケンスとして表現でき
ない場合でも動作するが、勿論性能は落ちる(この場合は警告される予定)
30
16:338:VectorAPI(Incubator)(Cont.)
※:SIMD:SingleInstruction,MultipleData.複数のデータに対して同じ操作を同時に実⾏する
ことを可能にする、1つのCPUサイクルでより多くの作業を実⾏できるようにすることで並列
度合いが向上する仕組み
利⽤するには --add-module jdk.incubator.vector が必要
公式のマイクロベンチマークテストコード:https://github.com/openjdk/panama-
vector/tree/vectorIntrinsics/test/jdk/jdk/incubator/vector/benchmark
ref.https://bugs.openjdk.java.net/browse/JDK-8244490
31
JVMchanges
Java15
374:DisableandDeprecateBiasedLocking
377:ZGC:AScalableLow-LatencyGarbageCollector
379:Shenandoah:ALow-Pause-TimeGarbageCollector
Java16
387:ElasticMetaspace
376:ZGC:ConcurrentThread-StackProcessing
32
15:374:DisableandDeprecateBiasedLocking
Java6からデフォルト有効だったBiasedLockingを無効化&⾮推奨化
将来的に関連オプションとともに削除
背景:
JVMは2つのロック実装(CAS(Compare-And-Swap)atomicinstructionを利⽤したアプ
ローチとBiasedLocking)を持っている
Biasedlockingは⾮競合状態のオーバヘッドを減らすために導⼊された
昔のアプリケーションは⾮競合状態でロックが取得されていることが多かった
⾮競合スレッドに偏らせて(Biased)ロックを配置してしまえば同じスレッドから早
くロックが取得できる可能性が⾼い
Java6時代はCAS命令は⾼コストだった上、昔のJavaAPIは並列処理向けが充実し
ておらず実際に効果があった
現在はむしろ逆効果なため、競合している場合はCAS命令を使⽤したアプローチへ
メンテナンスコストも改善できる 33
15:374:DisableandDeprecateBiasedLocking(Cont.)
利点:何もせずに⾼速化
有効にしたい場合は -XX:+UseBiasedLocking を指定すればOK
34
16:387:ElasticMetaspace
Metaspaceのメモリフットプリントなどを改善
背景:Metaspaceはヒープ外メモリを多く消費するユースケースがある
メタデータは通常、クラスがロードされたときに確保され、その⽣存期間はクラス
ローダーに依存する(⼀蓮托⽣)
Metaspaceのチャンクはアロケーション操作を効率的に⾏うため粒度が粗い
クラスローダが回収されると、Metaspaceのチャンクは後で再利⽤するためにフリ
ーリストに配置される
結果、頻繁にクラスローディングとアンローディングを⾏うアプリケーションは、
⼤量の使わないスペースをMetaspaceのフリーリスト内に蓄積してしまう
35
16:387:ElasticMetaspace(Cont.)
⽅法:メモリアロケータをbuddyallocationに置換え
より⼩さなチャンクでメタスペースメモリを割り当てられるようになり、クラスロ
ーダのオーバーヘッドを減らせる
同様にフラグメンテーションが減らせ、未使⽤のメタスペースメモリをより迅速に
OSに戻せるようになる(※元々断⽚化されてなかったらOSに返す場合があった)
利点:何もせずにJavaアプリケーションのメモリ使⽤量が改善
ref.https://cr.openjdk.java.net/~stuefe/JEP-Improve-Metaspace-Allocator/review-
guide/review-guide-1.0.html
36
15:379:Shenandoah:ALow-Pause-TimeGarbageCollector
G1GCのようにリージョン単位でヒープを管理しつつ並列コンパクションを採⽤したGC
ProductionReady(-XX:+UnlockExperimentalVMOptionsが不要)
デフォルトGCは変更なし(G1GC)
開発プロセスも変更なし(元々RedHatが主体で開発しておりRHELとRHELのdownstreamで
使われていた)
37
15:377:ZGC:AScalableLow-LatencyGarbageCollector
テラバイト級までの巨⼤なヒープ(最⼤16TB)を扱いながら、アプリケーション停⽌時間を10
ミリ秒以下にすることを⽬的としたGC
ProductionReady(-XX:+UnlockExperimentalVMOptionsが不要)
デフォルトGCは変更なし(G1GC)
Java11で試験導⼊されてから(Java12)59/(13)81/(14)101/(15)68件の改善が⾏われた。
Java15ではNVRAM(JDK-8239129)、Compressedclasspointer(JDK-8241825)、Class
DataSharing(JDK-8232069)のサポートや性能向上など
38
16:376:ZGC:ConcurrentThread-StackProcessing
背景:ZGCのSTWをまだ減らせる
利点:ZGCを使っていれば何もせず⾼速化する可能性がある
⽅法:スレッドスタック処理をsafepointから並⾏フェーズに移⾏
39
16:376:ZGC:ConcurrentThread-StackProcessing(Cont.)
ざっくりとした前提知識
GCは各ヒープオブジェクトへのポインタに関する情報が必要
これはJITコンパイラによって作成されたオブジェクトマップ(OopMap)に含まれる
JITによってコンパイルされた各メソッドは、特定の場所にOopMapを記録し、スタック
とレジスタのどの場所が参照されてるかを記録する
GCは、スタックをスキャンするときにこれらのOopMapを照会して、参照するポインタ
がどこにあるかを把握する
GC時に各スレッドがポインタを触っていると予期せぬ領域に対して処理が⾛る可能性が
あるので、そのような(unsafeな)スレッドはサスペンドする必要がある
各スレッドにsafepointに関する状態と遷移時の処理を持たせ、safepointlockを設ける
ことでGCなどのVMThread処理(vmoperation)を⾏う際に、各スレッドをblockする(そ
してVMThreadがsafepointにて処理を⾏う)
結果的に各Javaスレッドが⽌まってる状態なのでvmoperation≒ safepoint≒ STWと
なっている 40
16:376:ZGC:ConcurrentThread-StackProcessing(Cont.)
元々ZGCはマーキング処理などヒープサイズやメタスペースサイズに応じて増⼤化する
GC処理を、safepointでの操作から追い出して並⾏フェーズ(JavaスレッドとGCスレッ
ドを並⾏に処理)に移⾏させることでヒープサイズに関わらず短時間のSTWを達成してき
た
ルート処理などの⼀部がまだsafepointで⾏われている。ルートオブジェクト数はヒープ
サイズ依存ではないが、主にスレッド数などに依存するので⼤規模アプリでは問題にな
りえる
stackwatermarkbarrierを利⽤してGCがすべてのスレッドスタックと他のスレッドルー
トを処理をしている際にJavaスレッドがフレームに戻らないように整合性を確保するこ
とで並列化を実現
完成すれば他GCへの転⽤が望める
41
(おまけ)G1GCかZGCか
ZGCはJavaスレッドとGCスレッドを極⼒並列に動かすことでSTWを可能な限り削減し
ている
このためにJavaスレッドがオブジェクトを参照する際にLoad/ReadBarrierが必要なため
スループット低下が発⽣する
ポイントは
CPUリソースに余⼒はあるか
99.9%tileのレスポンスタイムとスループットのどちらが重要か
容易にスケールアウトできる構成になっているか
そもそも巨⼤ヒープが必要か
HBaseなどのストレージ系はキャッシュがあるのがパフォーマンス的に望ましい
それ以外はスケールアウトも考えると1プロセスあたりは多くなくても良い場合も
ZGCは少なくともJava15では8MBから対応している
G1GCもJava15で52件の対応が⾏われてるので⽐較が望ましい 42
Otherchanges
Java15
339:Edwards-CurveDigitalSignatureAlgorithm(EdDSA)
372:RemovetheNashornJavaScriptEngine
RhinoからNashorn、そして廃⽌へ
ECMAScriptの仕様変更のスピードに追従できなかった
GraalJSへの移⾏を推奨
381:RemovetheSolarisandSPARCPorts
43
Otherchanges(Cont.)
Java16
386:AlpineLinuxPort
ProjectPortola
ServiceabilityAgentのAttach機構が動かないので注意
388:Windows/AArch64Port
357:MigratefromMercurialtoGit
369:MigratetoGitHub
https://github.com/openjdk/
347:EnableC++14LanguageFeatures
Hotspot開発者に関係するものでJava利⽤者には直接関係はない
44
HeadtowardJava15andJava16
KUBOTAYuji
LINECorporation
JJUGCCC2020Fall(2020/Nov/7)
45
(おまけ)収録NG集
無⾳声になっているのにしばらく気付かなかった
普通にめっちゃ噛んでリテイク
laptopが熱暴⾛(アイスノンを置いた)
laptopがめっちゃ唸る
パトカーが建物そばを駆け抜けていく
運動会の練習(ピストル付き)
マイクに意図せずにぶつかる
OKGoogle,pleaseshutup
46

Head toward Java 15 and Java 16