SlideShare a Scribd company logo
1 of 62
Download to read offline
JDK 16で導入されたJEP 396にご注意!!
JEP 396 : Strongly Encapsulate JDK Internals by Default
JJUG CCC 2021 Spring
2021年5月23日
徳益芳郎
Yoshiro Tokumasu
Software Engineer
https://www.slideshare.net/tokumasu123/presentations
使ってみよう!JDK Flight Recorder
2018年12月11日
徳益 芳郎
Duke 出典:An Oracle blog about Oracle Enterprise Pack for Eclipse「Java 8 Launch!」
ログ出力を改めて考える
- JDK Flight Recorder の活用 -
2019年6月27日
徳益 芳郎
Duke 出典:An Oracle blog about Oracle Enterprise Pack for Eclipse「Java 8 Launch!」
はじめに
2021年3月リリースのJDK 16では、17個の JEP(JDK Enhancement Proposal)
が導入されました。導入された JEP に関する個々の詳細は、OpenJDK サイト
の JDK 16 をご覧ください。
JDK 16 で導入された JEP のうち、JEP 396 : Strongly Encapsulate JDK
Internals by Default による影響は十分に評価・準備することをお勧めします。
本セッションでは、JEP 396での変更点やその背景を解説すると共に、アプリ
ケーションでの評価・確認する際のポイントをご紹介します。
3
JEP 396 :
Strongly Encapsulate JDK Internals by Default
このような警告メッセージを
見たことありませんか?
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.jruby.ext.openssl.SecurityHelper (file:/tmp/jruby-1/
jruby5029500797019692358jopenssl.jar) to field java.security.MessageDigest.provider
WARNING: Please consider reporting this to the maintainers of
org.jruby.ext.openssl.SecurityHelper
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access
operations
WARNING: All illegal access operations will be denied in a future release
5
JEP 396での変更点
従来(JDK 9 ∼ 15)
--illegal-access=permit をデフォルト設定としてJava VMが起動する
今後(JDK 16以降)
--illegal-access=deny をデフォルト設定としてJava VMが起動する
6
JEP 396による影響
モジュール化していないアプリケーションやライブラリ(OSSを含む)にて、
sun.* や com.sun.* パッケージを含むJDK内部APIを利用している場合、
アプリケーション実行時に InaccessibleObjectException などの
ランタイム例外が発生する
7
JEP 396の背景
JDK 9リリース時に導入されたモジュール化ですが、完全なカプセル化を
強制すると影響が大きいということで、妥協した状態(--illegal-
access=permit:Relaxed strong encapsulation)で導入され、JDK内部
APIを利用しない実装へ改修するための移行期間を設けていた
しかし次のLTS(Long Term Support)候補であるJDK 17のリリース
(2021年9月予定)が見えてきた今(JDK 16 リリース時)、次の段階
(--illegal-access=deny:Strong encapsulation)に進むことになる
8
モジュールに対するアクセス制御
モジュールシステムの基本
モジュール化
従来のJARファイル
module foo {
exports bar;
opens fizz;
requires buzz;
}
モジュール定義
module-info
モジュラJARファイル
10
アクセス制御はモジュール定義に従う
module-
info
module-
info
Direct
Reflection
Deep

Reflection
module bar {
exports a;
}
module-
info
module fizz {
exports c;
opens c;
}
module foo {
requires bar;
requires buzz;
}
module-
info
module buzz {
exports b;
opens b;
}
<凡例>  :モジュール定義に従いアクセスできない(例外発生) 11
foo bar
fizz
buzz
モジュールの分類
モジュールシステムにおける経過措置
モジュール対応状況と配備場所
Unnamed Modules
--class-path --module-path
従来のJARファイル 従来のJARファイル
module-
info
モジュラJARファイル
module-
info
モジュラJARファイル
module-
info
Automatic Modules
Named Modules
無視
生成
13
JARファイル名 モジュール名
module-
info
生成
モジュール名がない!
module-
info
生成
モジュール名がない!
Unnamed / Automatic Modulesの扱い
モジュール定義 Unnamed Modules Automatic Modules
name なし
Jarファイル名 または、
JarファイルのMANIFEST属性で指定
requires
ALL-MODULE-PATH
ALL-UNNAMED
ALL-SYSTEM
ALL-MODULE-PATH
ALL-UNNAMED
ALL-SYSTEM
exports *(全て公開) *(全て公開)
opens *(全て許可) *(全て許可)
14
モジュールの分類
System Modules
Unnamed Modules Automatic Modules
Named Modules
module-
info
fizz
module-
info
module-
info
15
module-
info
⁉
モジュール名がない!
経過措置:将来は削除される予定だが、JDK 16 でも継続される
JARファイル名 モジュール名
モジュール分類間のアクセス制御
Illegal-access が発生する箇所(モジュール分類間)を知る!
モジュールの分類
System Modules
Unnamed Modules Automatic Modules
Named Modules
module-
info
fizz
module-
info
module-
info
17
module-
info
⁉
モジュール名がない! JARファイル名 モジュール名
再掲載
module-
info
Automatic Modules
Unnamed Modules → Automatic Modules
Unnamed Modules
18
⁉
module-
info
モジュール名がない!
Unnamed Modules は、
Automatic Modules を含む全てのモジュールを
参照(requires)と定義されている
JARファイル名 モジュール名
module fileA {
exports *;
opens *;
}
全てのパッケージを公開
全てのパッケージへの Deep Reflection を許可
<凡例>   :モジュール定義に従いアクセス制御される
Direct
Reflection
Deep

Reflection
module-
info
Automatic Modules
Automatic Modules → Unnamed Modules
Unnamed Modules
19
Direct
Reflection
Deep

Reflection
module ⁉ {
exports *;
opens *;
}
全てのパッケージを公開
全てのパッケージへの Deep Reflection を許可
⁉
module-
info
Automatic Modules は、
Unnamed Modules を含む全てのモジュールを
参照(requires)と定義されている
モジュール名がない! JARファイル名 モジュール名
<凡例>   :モジュール定義に従いアクセス制御される
Unnamed Modules → Named Modules
⁉
module-
info
モジュール名がない!
fizz
module-
info
Named Modules
Unnamed Modules は、
Named Modules を含む全てのモジュールを
参照(requires)と定義されている
<凡例>   :モジュール定義に従いアクセス制御される
Direct
Reflection
Deep

Reflection
module fizz {
exports D;
opens D;
}
モジュール定義の内容は、
モジュール毎に異なる
Unnamed Modules
20
module-
info
Automatic Modules
Automatic Modules → Named Modules
21
JARファイル名 モジュール名
<凡例>   :モジュール定義に従いアクセス制御される
fizz
module-
info
Named Modules
Direct
Reflection
Deep

Reflection
Automatic Modules は、
Named Modules を含む全てのモジュールを
参照(requires)と定義されている
module fizz {
exports D;
opens D;
}
モジュール定義の内容は、
モジュール毎に異なる
Named Modules → Unnamed Modules
Unnamed Modules
fizz
module-
info
Named Modules
22
Direct
Reflection
Deep

Reflection
⁉
<凡例>  :Named Modules のモジュール定義にて requires 指定できないので、アクセスできない
module ⁉ {
exports *;
opens *;
}
Unnamed Modules は、
モジュール名がない
module-
info
モジュール名がない!
Named Modules は、
参照したいモジュールを
requires で定義する必要がある
module-
info
Automatic Modules
Named Modules → Automatic Modules
23
JARファイル名 モジュール名
<凡例>   :モジュール定義に従いアクセス制御される
fizz
module-
info
Named Modules
module fizz {
exports D;
opens D;
requires fileA;
}
Named Modules は、
参照したいモジュールを
requires で定義する必要がある
module fileA {
exports *;
opens *;
}
全てのパッケージを公開
全てのパッケージへの Deep Reflection を許可
Direct
Reflection
Deep

Reflection
module-
info
Automatic Modules
Automatic Modules → System Modules
24
Direct
Reflection
Deep

Reflection
Automatic Modules は、
System Modules を含む全てのモジュールを
参照(requires)と定義されている
JARファイル名 モジュール名
System Modules
module-
info
module buzz {
exports D;
}
モジュール定義の内容は、
モジュール毎に異なる
<凡例>   :モジュール定義に従いアクセス制御される
Unnamed Modules → System Modules
25
System Modules
module-
info
Unnamed Modules
⁉
module-
info
モジュール名がない!
module buzz {
exports D;
}
Unnamed Modules は、
System Modules を含む全てのモジュールを
参照(requires)と定義されている
Direct
Reflection
Deep

Reflection
!
--illegal-access=permit
JDK 15 以前の挙動
! !
<凡例>   :モジュール定義に反して常にアクセス可能(警告メッセージ)
!
モジュール定義に反して常にアクセス可能
$ java --show-version Main2.java
openjdk 15.0.2 2021-01-19
OpenJDK Runtime Environment (build 15.0.2+7-27)
OpenJDK 64-Bit Server VM (build 15.0.2+7-27, mixed mode, sharing)
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to method
java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean)
WARNING: Please consider reporting this to the maintainers of Main2
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
26
Unnamed Modules → System Modules
27
System Modules
module-
info
Unnamed Modules
⁉
module-
info
モジュール名がない!
module buzz {
exports D;
}
モジュール定義の内容は、
モジュール毎に異なる
Unnamed Modules は、
System Modules を含む全てのモジュールを
参照(requires)と定義されている
Direct
Reflection
Deep

Reflection
--illegal-access=deny
JDK 16 以降の挙動
<凡例>   :モジュール定義に従いアクセス制御される
モジュール定義に従いアクセス制御される
$ java --show-version Main2.java
openjdk 16 2021-03-16
OpenJDK Runtime Environment (build 16+35-2229)
OpenJDK 64-Bit Server VM (build 16+35-2229, mixed mode, sharing)
java.lang.reflect.InaccessibleObjectException: Unable to make private boolean
java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean) accessible: module java.base
does not "opens java.util.concurrent" to unnamed module @7f416310
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:357)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
at Main3.main(Main2.java:18)
28
RuntimeException
JDK内部API
System Modules の非公開な APIのうち、
--illegal-access=deny の影響を受ける箇所(分類)を知る!
JDK内部API
本来、アプリケーションやライブラリで利用されることを想定していない
非公開な API であり、sun.* や com.sun.* または jdk.* パッケージなど…
ここでは、以下の3つに分類する
代替APIが存在する
影響が大きい
その他
30
System Modules
module-
info
代替APIが存在する JDK内部API
https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool 31
JDK 8の時点で既に代替APIが提供されており、現在は削除されている
標準API
JDK内部API
標準API
JDK内部API
影響が大きい JDK内部API
重要な機能を提供しているが、現時点でも代替APIが存在しない…
経過措置:jdk.unsupported モジュールに格納されて提供されている
将来は削除される予定だが、JDK 16 でも継続される
sun.misc.Signal
sun.misc.SignalHandler
sun.misc.Unsafe
sun.reflect.Reflection::getCallerClass(int)
sun.reflect.ReflectionFactory
com.sun.nio.file.ExtendedCopyOption
com.sun.nio.file.ExtendedOpenOption
com.sun.nio.file.ExtendedWatchEventModifier
com.sun.nio.file.SensitivityWatchEventModifier
$ java --describe-module jdk.unsupported
jdk.unsupported@16
exports com.sun.nio.file
exports sun.misc
exports sun.reflect
requires java.base mandated
opens sun.misc
opens sun.reflect
JDK 9 で代替APIが提供され
たのでJDK 11で削除された
32
その他 JDK内部API
① 他の分類に該当しない、sun.* や com.sun.* パッケージなどの非公開パッケージ
JDK 9以降の環境では、基本的にカプセル化されている(アクセス不可)
経過措置(JDK15まで):実行時に限り、Unnamed module に対して公開 (exports)
② 全パッケージに含まれる private クラスやメソッド、フィールドなど…
Deep Reflection によるアクセスを必須とする
経過措置(JDK15まで):Unnamed module に対して deep reflection を許可 (opens)
33
JDK内部APIの分類とその影響
JEP 396(--illegal-access のパラメータを permit から deny に変更)の影響
がJDK内部APIの分類に応じて異なる
JDK内部APIの分類 --illegal-access=permit --illegal-access=deny
代替APIが存在する ❌
(NoClassDefFoundError などが発生)
❌
(NoClassDefFoundError などが発生)
影響が大きい
(jdk.unsupported モジュール)
✔ ✔
その他 ✔ ❌
(InaccessibleObjectException などが発生)
<凡例> ✔:問題なくアクセス可能 ❌:アクセス不可(例外が発生する)
34
JDK 16 のデフォルト値
--illegal-access=<parameter>
Java VM 起動オプション
Unnamed Module による許可されていないJDK内部APIへのアクセスに対
する挙動を、パラメータ値に応じて制御する
--illegal-access のパラメータと挙動
パラメータ値 初回アクセス 2回目以降のアクセス 補足
permit △ ✔ JDK 9 ∼15のデフォルト値
warn △ △ ー
debug
△
(スタックトレースも出力)
△
(スタックトレースも出力)
ー
deny ❌ ❌ JDK 16以降のデフォルト値
<凡例> ✔:問題なくアクセス可能 △:アクセス可能(但し、警告メッセージを出力) ❌:アクセス不可(例外が発生する)
36
--illegal-access=warn の場合
$ java --show-version --illegal-access=warn Main2.java
openjdk 15.0.2 2021-01-19
OpenJDK Runtime Environment (build 15.0.2+7-27)
OpenJDK 64-Bit Server VM (build 15.0.2+7-27, mixed mode, sharing)
WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to method
java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean)
WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to method
java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean)
WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to field
com.sun.crypto.provider.BlowfishConstants.BLOWFISH_BLOCK_SIZE
37
警告メッセージ(許可されないアクセスの回数分)
--illegal-access=debug の場合
$ java --show-version --illegal-access=debug Main2.java
openjdk 15.0.2 2021-01-19
OpenJDK Runtime Environment (build 15.0.2+7-27)
OpenJDK 64-Bit Server VM (build 15.0.2+7-27, mixed mode, sharing)
WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to method
java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean)
at Main2.main(Main2.java:18)
WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to method
java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean)
at Main2.main(Main2.java:26)
WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to field
com.sun.crypto.provider.BlowfishConstants.BLOWFISH_BLOCK_SIZE
at Main2.main(Main2.java:35)
38
警告メッセージとスタックトーレス(許可されないアクセスの回数分)
影響ある箇所の特定方法は⁉
その他 に分類されるJDK内部APIを利用する箇所を特定する
JDK内部APIの分類とその影響
JEP 396(--illegal-access のパラメータを permit から deny に変更)の影響
がJDK内部APIの分類に応じて異なる
JDK内部APIの分類 --illegal-access=permit --illegal-access=deny
代替APIが存在する ❌
(NoClassDefFoundError などが発生)
❌
(NoClassDefFoundError などが発生)
影響が大きい
(jdk.unsupported モジュール)
✔ ✔
その他 ✔ ❌
(InaccessibleObjectException などが発生)
<凡例> ✔:問題なくアクセス可能 ❌:アクセス不可(例外が発生する)
40
JDK 16 のデフォルト値
再掲載
JDK内部APIを利用するコード例
try {
sun.misc.BASE64Encoder b64 = new sun.misc.BASE64Encoder();
System.out.println(b64.encode(new byte[]{1, 2, 3}));
} catch (Throwable ex) { ex.printStackTrace(); }
try {
Field field = sun.misc.Unsafe.class.getDeclaredField(“theUnsafe");
field.setAccessible(true);
sun.misc.Unsafe unsafe = (sun.misc.Unsafe) field.get(null);
} catch (Exception ex) { ex.printStackTrace(); }
try {
java.util.conccurent.ThreadPoolExecutor executor =
new ThreadPoolExecutor(5, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
Method addWorker = ThreadPoolExecutor.class.getDeclaredMethod(“addWorker", Runnable.class, Boolean.TYPE);
addWorker.setAccessible(true);
addWorker.invoke(executor, null, Boolean.FALSE);
} catch (Exception ex) { ex.printStackTrace(); }
その他
41
Deep Reflection
代替APIが存在する
影響が大きい
jdeps ツールで確認⁉
$ jdeps --jdk-internals --class-path test.legacy.main.jar
test.legacy.main.jar -> jdk8internals
test.legacy.main.jar -> jdk.unsupported
test.main.Main -> sun.misc.BASE64Encoder JDK internal API (jdk8internals)
test.main.Main -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
警告: JDK内部APIはサポートされておらず、JDK実装専用ですが、互換性なしで
削除または変更される場合があり、アプリケーションを中断させる可能性があります。
JDK内部APIの依存性を削除するようコードを変更してください。
JDK内部APIの置換に関する最新の更新については、次を確認してください:
https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool
JDK内部API 修正候補
-------- ----
sun.misc.BASE64Encoder Use java.util.Base64 @since 1.8
sun.misc.Unsafe See http://openjdk.java.net/jeps/260
42
jdeps ツールで確認する際の注意点
--jdk-internals オプションで確認できる JDK内部API の種類は限られる!
JDK内部APIの分類 jdepsツールでの確認 例
代替APIが存在する ✔ sun.misc.BASE64Decoder
sun.reflect.Reflection.getCallerClass
影響が大きい
(jdk.unsupported モジュール)
✔ sun.misc.Unsafe
com.sun.nio.file.ExtendedCopyOption
その他 ❌
java.util.concurrent.ThreadPoolExecutor.addWorker
java.security.MessageDigest.provider
sun.security.util.SecurityConstants
<凡例> ✔:確認できる ❌:確認できない
43
direct アクセスの箇所のみ検出できる
⁉
setAccessible(true) の箇所を特定⁉
全てのソースコード(商用版を含む)を保持しているなら可能かもねぇ…
OSSの場合でもマイナーバージョンまで完全把握していないとねぇ…
System modules 以外のモジュールに対する箇所もヒットするけどねぇ…
やらないより、やる価値はあるので、自作のソースコードだけでも!
44
JDK 16 以降に向けた準備・確認
以下のように JEP 396 による影響を確認する
1.--illegal-access=debug を明示的に指定して Java VM を起動する
警告メッセージとスタックトレースを基に修正すべき箇所を確認する
2.--illegal-access=deny を明示的に指定して Java VM を起動する
例外が発生することなく、アプリケーションが正常動作することを確認する
45
JDK 16 で実行してみた…
JDK 16 では、やっぱり例外発生!
$ java --show-version Main3.java
openjdk 16 2021-03-16
OpenJDK Runtime Environment (build 16+35-2229)
OpenJDK 64-Bit Server VM (build 16+35-2229, mixed mode, sharing)
java.lang.reflect.InaccessibleObjectException: Unable to make private boolean
java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean) accessible: module java.base
does not "opens java.util.concurrent" to unnamed module @7f416310
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:357)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
at Main3.main(Main3.java:18)
47
RuntimeException
ソースコード修正なしに対処するには…
$ java --show-version --add-opens java.base/java.util.concurrent=ALL-UNNAMED Main3.java
openjdk 16 2021-03-16
OpenJDK Runtime Environment (build 16+35-2229)
OpenJDK 64-Bit Server VM (build 16+35-2229, mixed mode, sharing)
48
この対処は、リスクをともなうので注意が必要である
モジュール提供者の意思に反している!
JDK内部APIが変更・削除された場合、失敗する!
JDK 16 では --illegal-access は deprecated
$ java --show-version --illegal-access=permit Main3.java
OpenJDK 64-Bit Server VM warning: Option --illegal-access is deprecated and will be removed in a future release.
openjdk 16 2021-03-16
OpenJDK Runtime Environment (build 16+35-2229)
OpenJDK 64-Bit Server VM (build 16+35-2229, mixed mode, sharing)
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by Main3 (file:/2021/Main3.java) to method
java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean)
WARNING: Please consider reporting this to the maintainers of Main3
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
49
警告メッセージ(初回アクセス分のみ)
まとめ
JEP 396での変更点
従来(JDK 9 ∼ 15)
--illegal-access=permit をデフォルト設定としてJava VMが起動する
今後(JDK 16以降)
--illegal-access=deny をデフォルト設定としてJava VMが起動する
51
再掲載
Unnamed Modules → System Modules
52
System Modules
module-
info
Unnamed Modules
⁉
module-
info
モジュール名がない!
module buzz {
exports D;
}
モジュール定義の内容は、
モジュール毎に異なる
Unnamed Modules は、
System Modules を含む全てのモジュールを
参照(requires)と定義されている
Direct
Reflection
Deep

Reflection
--illegal-access=permit
JDK 15 以前の挙動 再掲載
! !
<凡例>   :モジュール定義に反して常にアクセス可能(警告メッセージ)
!
!
Unnamed Modules → System Modules
53
System Modules
module-
info
Unnamed Modules
⁉
module-
info
モジュール名がない!
module buzz {
exports D;
}
モジュール定義の内容は、
モジュール毎に異なる
Unnamed Modules は、
System Modules を含む全てのモジュールを
参照(requires)と定義されている
Direct
Reflection
Deep

Reflection
--illegal-access=deny
JDK 16 以降の挙動
<凡例>   :モジュール定義に従いアクセス制御される
再掲載
その他 JDK内部API
① 他の分類に該当しない、sun.* や com.sun.* パッケージなどの非公開パッケージ
JDK 9以降の環境では、基本的にカプセル化されている(アクセス不可)
経過措置(JDK15まで):実行時に限り、Unnamed module に対して公開 (exports)
② 全パッケージに含まれる private クラスやメソッド、フィールドなど…
Deep Reflection によるアクセスを必須とする
経過措置(JDK15まで):Unnamed module に対して deep reflection を許可 (opens)
54
再掲載
JDK 16 以降に向けた準備・確認
以下のように JEP 396 による影響を確認する
1.--illegal-access=debug を明示的に指定して Java VM を起動する
警告メッセージとスタックトレースを基に対処すべき箇所を確認する
2.--illegal-access=deny を明示的に指定して Java VM を起動する
例外が発生することなく、アプリケーションが正常動作することを確認する
55
再掲載
--add-opens/--add-exports のリスク
$ java --show-version --add-opens java.base/java.util.concurrent=ALL-UNNAMED Main2.java
openjdk 16 2021-03-16
OpenJDK Runtime Environment (build 16+35-2229)
OpenJDK 64-Bit Server VM (build 16+35-2229, mixed mode, sharing)
56
この対処は、リスクをともなうので注意が必要である
モジュール提供者の意思に反している!
JDK内部APIが変更・削除された場合、失敗する!
APPENDIX
参考情報
JDK 16 - OpenJDK -
https://openjdk.java.net/projects/jdk/16/
JEP 396: Strongly Encapsulate JDK Internals by Default
https://openjdk.java.net/jeps/396
Relaxed Strong encapsulation - JEP 261: Module System -
https://openjdk.java.net/jeps/261#Relaxed-strong-encapsulation
58
参考情報
JEP 260: Encapsulate Most Internal APIs
https://openjdk.java.net/jeps/260
Replace uses of the JDK’s internal APIs - Java Dependency Analysis Tool -
https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool
Internal packages that will no longer be open by default 

/ Exported packages that will no longer be open by default
https://cr.openjdk.java.net/~mr/jigsaw/jdk8-packages-denied-by-default
59
その他
使ってみよう!JDK Flight Recorder
https://www.slideshare.net/tokumasu123/jdk-flight-recorder-126001298
ログ出力を改めて考える - JDK Flight Recorder の活用 -
https://www.slideshare.net/tokumasu123/jdk-flight-recorder-155345312
JFR Event Streaming によるAP監視 - JDK Flight Recorder の活用 -
https://www.slideshare.net/tokumasu123/jfr-event-streamingap-jdk-flight-recorder
60
END
“JDK 16 で導入された JEP 396 にご注意”
JEP 396 : Strongly Encapsulate JDK Internals by Default
JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)

More Related Content

What's hot

PostgreSQL初心者がパッチを提案してからコミットされるまで(第20回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQL初心者がパッチを提案してからコミットされるまで(第20回PostgreSQLアンカンファレンス@オンライン 発表資料)PostgreSQL初心者がパッチを提案してからコミットされるまで(第20回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQL初心者がパッチを提案してからコミットされるまで(第20回PostgreSQLアンカンファレンス@オンライン 発表資料)NTT DATA Technology & Innovation
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugMasatoshi Tada
 
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)NTT DATA Technology & Innovation
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーyoku0825
 
CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...
CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...
CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...NTT DATA Technology & Innovation
 
VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)
VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)
VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)NTT DATA Technology & Innovation
 
jcmd をさわってみよう
jcmd をさわってみようjcmd をさわってみよう
jcmd をさわってみようTsunenaga Hanyuda
 
JDKの選択肢とサーバーサイドでの選び方
JDKの選択肢とサーバーサイドでの選び方JDKの選択肢とサーバーサイドでの選び方
JDKの選択肢とサーバーサイドでの選び方Takahiro YAMADA
 
Garbage First Garbage Collection (G1 GC) #jjug_ccc #ccc_cd6
Garbage First Garbage Collection (G1 GC) #jjug_ccc #ccc_cd6Garbage First Garbage Collection (G1 GC) #jjug_ccc #ccc_cd6
Garbage First Garbage Collection (G1 GC) #jjug_ccc #ccc_cd6Yuji Kubota
 
MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法Tetsutaro Watanabe
 
コンテナを止めるな! PacemakerによるコンテナHAクラスタリングとKubernetesとの違いとは
コンテナを止めるな!  PacemakerによるコンテナHAクラスタリングとKubernetesとの違いとはコンテナを止めるな!  PacemakerによるコンテナHAクラスタリングとKubernetesとの違いとは
コンテナを止めるな! PacemakerによるコンテナHAクラスタリングとKubernetesとの違いとはksk_ha
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション土岐 孝平
 
DockerとPodmanの比較
DockerとPodmanの比較DockerとPodmanの比較
DockerとPodmanの比較Akihiro Suda
 
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)NTT DATA Technology & Innovation
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Akihiro Suda
 
JAVA_HOME/binにあるコマンド、いくつ使っていますか?[JVM関連ツール編](JJUGナイトセミナー「Java解析ツール特集」 発表資料)
JAVA_HOME/binにあるコマンド、いくつ使っていますか?[JVM関連ツール編](JJUGナイトセミナー「Java解析ツール特集」 発表資料)JAVA_HOME/binにあるコマンド、いくつ使っていますか?[JVM関連ツール編](JJUGナイトセミナー「Java解析ツール特集」 発表資料)
JAVA_HOME/binにあるコマンド、いくつ使っていますか?[JVM関連ツール編](JJUGナイトセミナー「Java解析ツール特集」 発表資料)NTT DATA Technology & Innovation
 
単なるキャッシュじゃないよ!?infinispanの紹介
単なるキャッシュじゃないよ!?infinispanの紹介単なるキャッシュじゃないよ!?infinispanの紹介
単なるキャッシュじゃないよ!?infinispanの紹介AdvancedTechNight
 
GraalVMを3つの主機能から眺めてみよう(Oracle Groundbreakers APAC Virtual Tour 2020 講演資料)
GraalVMを3つの主機能から眺めてみよう(Oracle Groundbreakers APAC Virtual Tour 2020 講演資料)GraalVMを3つの主機能から眺めてみよう(Oracle Groundbreakers APAC Virtual Tour 2020 講演資料)
GraalVMを3つの主機能から眺めてみよう(Oracle Groundbreakers APAC Virtual Tour 2020 講演資料)NTT DATA Technology & Innovation
 
SQL Server コンテナ入門(Kubernetes編)
SQL Server コンテナ入門(Kubernetes編)SQL Server コンテナ入門(Kubernetes編)
SQL Server コンテナ入門(Kubernetes編)Tomoyuki Oota
 

What's hot (20)

PostgreSQL初心者がパッチを提案してからコミットされるまで(第20回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQL初心者がパッチを提案してからコミットされるまで(第20回PostgreSQLアンカンファレンス@オンライン 発表資料)PostgreSQL初心者がパッチを提案してからコミットされるまで(第20回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQL初心者がパッチを提案してからコミットされるまで(第20回PostgreSQLアンカンファレンス@オンライン 発表資料)
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsug
 
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキー
 
CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...
CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...
CloudNativePGを動かしてみた! ~PostgreSQL on Kubernetes~(第34回PostgreSQLアンカンファレンス@オンライ...
 
VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)
VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)
VSCodeで作るPostgreSQL開発環境(第25回 PostgreSQLアンカンファレンス@オンライン 発表資料)
 
jcmd をさわってみよう
jcmd をさわってみようjcmd をさわってみよう
jcmd をさわってみよう
 
JDKの選択肢とサーバーサイドでの選び方
JDKの選択肢とサーバーサイドでの選び方JDKの選択肢とサーバーサイドでの選び方
JDKの選択肢とサーバーサイドでの選び方
 
Garbage First Garbage Collection (G1 GC) #jjug_ccc #ccc_cd6
Garbage First Garbage Collection (G1 GC) #jjug_ccc #ccc_cd6Garbage First Garbage Collection (G1 GC) #jjug_ccc #ccc_cd6
Garbage First Garbage Collection (G1 GC) #jjug_ccc #ccc_cd6
 
MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法
 
コンテナを止めるな! PacemakerによるコンテナHAクラスタリングとKubernetesとの違いとは
コンテナを止めるな!  PacemakerによるコンテナHAクラスタリングとKubernetesとの違いとはコンテナを止めるな!  PacemakerによるコンテナHAクラスタリングとKubernetesとの違いとは
コンテナを止めるな! PacemakerによるコンテナHAクラスタリングとKubernetesとの違いとは
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション
 
DockerとPodmanの比較
DockerとPodmanの比較DockerとPodmanの比較
DockerとPodmanの比較
 
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
 
JAVA_HOME/binにあるコマンド、いくつ使っていますか?[JVM関連ツール編](JJUGナイトセミナー「Java解析ツール特集」 発表資料)
JAVA_HOME/binにあるコマンド、いくつ使っていますか?[JVM関連ツール編](JJUGナイトセミナー「Java解析ツール特集」 発表資料)JAVA_HOME/binにあるコマンド、いくつ使っていますか?[JVM関連ツール編](JJUGナイトセミナー「Java解析ツール特集」 発表資料)
JAVA_HOME/binにあるコマンド、いくつ使っていますか?[JVM関連ツール編](JJUGナイトセミナー「Java解析ツール特集」 発表資料)
 
単なるキャッシュじゃないよ!?infinispanの紹介
単なるキャッシュじゃないよ!?infinispanの紹介単なるキャッシュじゃないよ!?infinispanの紹介
単なるキャッシュじゃないよ!?infinispanの紹介
 
GraalVMを3つの主機能から眺めてみよう(Oracle Groundbreakers APAC Virtual Tour 2020 講演資料)
GraalVMを3つの主機能から眺めてみよう(Oracle Groundbreakers APAC Virtual Tour 2020 講演資料)GraalVMを3つの主機能から眺めてみよう(Oracle Groundbreakers APAC Virtual Tour 2020 講演資料)
GraalVMを3つの主機能から眺めてみよう(Oracle Groundbreakers APAC Virtual Tour 2020 講演資料)
 
Helidon 概要
Helidon 概要Helidon 概要
Helidon 概要
 
SQL Server コンテナ入門(Kubernetes編)
SQL Server コンテナ入門(Kubernetes編)SQL Server コンテナ入門(Kubernetes編)
SQL Server コンテナ入門(Kubernetes編)
 

Similar to JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)

OpenJDKソムリエと巡るJDKワイナリーツアー #sfggjp #javajo
OpenJDKソムリエと巡るJDKワイナリーツアー #sfggjp #javajoOpenJDKソムリエと巡るJDKワイナリーツアー #sfggjp #javajo
OpenJDKソムリエと巡るJDKワイナリーツアー #sfggjp #javajoTakahiro YAMADA
 
JBoss AS 7 / EAP 6 modules and class loading
JBoss AS 7 / EAP 6 modules and class loadingJBoss AS 7 / EAP 6 modules and class loading
JBoss AS 7 / EAP 6 modules and class loadingnekop
 
Migration Guide from Java 8 to Java 11 #jjug
Migration Guide from Java 8 to Java 11 #jjugMigration Guide from Java 8 to Java 11 #jjug
Migration Guide from Java 8 to Java 11 #jjugYuji Kubota
 
Solaris 11 ディープダイブセミナー Distribution Constructor編
Solaris 11 ディープダイブセミナー Distribution Constructor編Solaris 11 ディープダイブセミナー Distribution Constructor編
Solaris 11 ディープダイブセミナー Distribution Constructor編SolarisJP
 
Android4.2徹底解剖!
Android4.2徹底解剖!Android4.2徹底解剖!
Android4.2徹底解剖!leverages_event
 
Appsody でnodejsのアプリを立ち上げよう!
Appsody でnodejsのアプリを立ち上げよう!Appsody でnodejsのアプリを立ち上げよう!
Appsody でnodejsのアプリを立ち上げよう!Daisuke Hiraoka
 
第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」
第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」 第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」
第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」 Kazuyuki Sato
 
第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」
第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」
第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」SolarisJP
 
Serviceability Toolsの裏側
Serviceability Toolsの裏側Serviceability Toolsの裏側
Serviceability Toolsの裏側Yasumasa Suenaga
 
WildFly Swarm - Rightsize Your Java EE Apps
WildFly Swarm - Rightsize Your Java EE AppsWildFly Swarm - Rightsize Your Java EE Apps
WildFly Swarm - Rightsize Your Java EE AppsYoshimasa Tanabe
 
SEAndroid -AndroidのアーキテクチャとSE化について-
SEAndroid -AndroidのアーキテクチャとSE化について-SEAndroid -AndroidのアーキテクチャとSE化について-
SEAndroid -AndroidのアーキテクチャとSE化について-Hiromu Yakura
 
5分で作るMySQL Cluster環境
5分で作るMySQL Cluster環境5分で作るMySQL Cluster環境
5分で作るMySQL Cluster環境yoyamasaki
 
5分で作るMySQL Cluster環境
5分で作るMySQL Cluster環境5分で作るMySQL Cluster環境
5分で作るMySQL Cluster環境yoyamasaki
 
Code igniterでテスト駆動開発 資料作成中
Code igniterでテスト駆動開発 資料作成中Code igniterでテスト駆動開発 資料作成中
Code igniterでテスト駆動開発 資料作成中Takako Miyagawa
 
今年はJava進化の年!今知っておくべき新しいJava
今年はJava進化の年!今知っておくべき新しいJava今年はJava進化の年!今知っておくべき新しいJava
今年はJava進化の年!今知っておくべき新しいJavaTakashi Ito
 
明日から使えるgradle
明日から使えるgradle明日から使えるgradle
明日から使えるgradlekimukou_26 Kimukou
 
【Swift】SDKの読み込みにuse framework!が使えない場合の対処方法
【Swift】SDKの読み込みにuse framework!が使えない場合の対処方法【Swift】SDKの読み込みにuse framework!が使えない場合の対処方法
【Swift】SDKの読み込みにuse framework!が使えない場合の対処方法natsumo
 
Oracle Solaris 11の可用性を高める! Oracle Solaris Cluster 4.0(補足資料)
Oracle Solaris 11の可用性を高める! Oracle Solaris Cluster 4.0(補足資料)Oracle Solaris 11の可用性を高める! Oracle Solaris Cluster 4.0(補足資料)
Oracle Solaris 11の可用性を高める! Oracle Solaris Cluster 4.0(補足資料)SolarisJP
 

Similar to JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring) (20)

OpenJDKソムリエと巡るJDKワイナリーツアー #sfggjp #javajo
OpenJDKソムリエと巡るJDKワイナリーツアー #sfggjp #javajoOpenJDKソムリエと巡るJDKワイナリーツアー #sfggjp #javajo
OpenJDKソムリエと巡るJDKワイナリーツアー #sfggjp #javajo
 
JBoss AS 7 / EAP 6 modules and class loading
JBoss AS 7 / EAP 6 modules and class loadingJBoss AS 7 / EAP 6 modules and class loading
JBoss AS 7 / EAP 6 modules and class loading
 
Migration Guide from Java 8 to Java 11 #jjug
Migration Guide from Java 8 to Java 11 #jjugMigration Guide from Java 8 to Java 11 #jjug
Migration Guide from Java 8 to Java 11 #jjug
 
Solaris 11 ディープダイブセミナー Distribution Constructor編
Solaris 11 ディープダイブセミナー Distribution Constructor編Solaris 11 ディープダイブセミナー Distribution Constructor編
Solaris 11 ディープダイブセミナー Distribution Constructor編
 
Clang Modules
Clang ModulesClang Modules
Clang Modules
 
Android4.2徹底解剖!
Android4.2徹底解剖!Android4.2徹底解剖!
Android4.2徹底解剖!
 
Appsody でnodejsのアプリを立ち上げよう!
Appsody でnodejsのアプリを立ち上げよう!Appsody でnodejsのアプリを立ち上げよう!
Appsody でnodejsのアプリを立ち上げよう!
 
第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」
第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」 第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」
第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」
 
第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」
第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」
第6回コンテナ型仮想化の情報交換@東京「今日から触れる Solaris Zones 入門」
 
Serviceability Toolsの裏側
Serviceability Toolsの裏側Serviceability Toolsの裏側
Serviceability Toolsの裏側
 
WildFly Swarm - Rightsize Your Java EE Apps
WildFly Swarm - Rightsize Your Java EE AppsWildFly Swarm - Rightsize Your Java EE Apps
WildFly Swarm - Rightsize Your Java EE Apps
 
SEAndroid -AndroidのアーキテクチャとSE化について-
SEAndroid -AndroidのアーキテクチャとSE化について-SEAndroid -AndroidのアーキテクチャとSE化について-
SEAndroid -AndroidのアーキテクチャとSE化について-
 
5分で作るMySQL Cluster環境
5分で作るMySQL Cluster環境5分で作るMySQL Cluster環境
5分で作るMySQL Cluster環境
 
5分で作るMySQL Cluster環境
5分で作るMySQL Cluster環境5分で作るMySQL Cluster環境
5分で作るMySQL Cluster環境
 
Code igniterでテスト駆動開発 資料作成中
Code igniterでテスト駆動開発 資料作成中Code igniterでテスト駆動開発 資料作成中
Code igniterでテスト駆動開発 資料作成中
 
今年はJava進化の年!今知っておくべき新しいJava
今年はJava進化の年!今知っておくべき新しいJava今年はJava進化の年!今知っておくべき新しいJava
今年はJava進化の年!今知っておくべき新しいJava
 
明日から使えるgradle
明日から使えるgradle明日から使えるgradle
明日から使えるgradle
 
【Swift】SDKの読み込みにuse framework!が使えない場合の対処方法
【Swift】SDKの読み込みにuse framework!が使えない場合の対処方法【Swift】SDKの読み込みにuse framework!が使えない場合の対処方法
【Swift】SDKの読み込みにuse framework!が使えない場合の対処方法
 
Driverについて
DriverについてDriverについて
Driverについて
 
Oracle Solaris 11の可用性を高める! Oracle Solaris Cluster 4.0(補足資料)
Oracle Solaris 11の可用性を高める! Oracle Solaris Cluster 4.0(補足資料)Oracle Solaris 11の可用性を高める! Oracle Solaris Cluster 4.0(補足資料)
Oracle Solaris 11の可用性を高める! Oracle Solaris Cluster 4.0(補足資料)
 

JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)

  • 1. JDK 16で導入されたJEP 396にご注意!! JEP 396 : Strongly Encapsulate JDK Internals by Default JJUG CCC 2021 Spring 2021年5月23日 徳益芳郎
  • 2. Yoshiro Tokumasu Software Engineer https://www.slideshare.net/tokumasu123/presentations 使ってみよう!JDK Flight Recorder 2018年12月11日 徳益 芳郎 Duke 出典:An Oracle blog about Oracle Enterprise Pack for Eclipse「Java 8 Launch!」 ログ出力を改めて考える - JDK Flight Recorder の活用 - 2019年6月27日 徳益 芳郎 Duke 出典:An Oracle blog about Oracle Enterprise Pack for Eclipse「Java 8 Launch!」
  • 3. はじめに 2021年3月リリースのJDK 16では、17個の JEP(JDK Enhancement Proposal) が導入されました。導入された JEP に関する個々の詳細は、OpenJDK サイト の JDK 16 をご覧ください。 JDK 16 で導入された JEP のうち、JEP 396 : Strongly Encapsulate JDK Internals by Default による影響は十分に評価・準備することをお勧めします。 本セッションでは、JEP 396での変更点やその背景を解説すると共に、アプリ ケーションでの評価・確認する際のポイントをご紹介します。 3
  • 4. JEP 396 : Strongly Encapsulate JDK Internals by Default
  • 5. このような警告メッセージを 見たことありませんか? WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by org.jruby.ext.openssl.SecurityHelper (file:/tmp/jruby-1/ jruby5029500797019692358jopenssl.jar) to field java.security.MessageDigest.provider WARNING: Please consider reporting this to the maintainers of org.jruby.ext.openssl.SecurityHelper WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release 5
  • 6. JEP 396での変更点 従来(JDK 9 ∼ 15) --illegal-access=permit をデフォルト設定としてJava VMが起動する 今後(JDK 16以降) --illegal-access=deny をデフォルト設定としてJava VMが起動する 6
  • 7. JEP 396による影響 モジュール化していないアプリケーションやライブラリ(OSSを含む)にて、 sun.* や com.sun.* パッケージを含むJDK内部APIを利用している場合、 アプリケーション実行時に InaccessibleObjectException などの ランタイム例外が発生する 7
  • 8. JEP 396の背景 JDK 9リリース時に導入されたモジュール化ですが、完全なカプセル化を 強制すると影響が大きいということで、妥協した状態(--illegal- access=permit:Relaxed strong encapsulation)で導入され、JDK内部 APIを利用しない実装へ改修するための移行期間を設けていた しかし次のLTS(Long Term Support)候補であるJDK 17のリリース (2021年9月予定)が見えてきた今(JDK 16 リリース時)、次の段階 (--illegal-access=deny:Strong encapsulation)に進むことになる 8
  • 10. モジュール化 従来のJARファイル module foo { exports bar; opens fizz; requires buzz; } モジュール定義 module-info モジュラJARファイル 10
  • 11. アクセス制御はモジュール定義に従う module- info module- info Direct Reflection Deep
 Reflection module bar { exports a; } module- info module fizz { exports c; opens c; } module foo { requires bar; requires buzz; } module- info module buzz { exports b; opens b; } <凡例>  :モジュール定義に従いアクセスできない(例外発生) 11 foo bar fizz buzz
  • 13. モジュール対応状況と配備場所 Unnamed Modules --class-path --module-path 従来のJARファイル 従来のJARファイル module- info モジュラJARファイル module- info モジュラJARファイル module- info Automatic Modules Named Modules 無視 生成 13 JARファイル名 モジュール名 module- info 生成 モジュール名がない! module- info 生成 モジュール名がない!
  • 14. Unnamed / Automatic Modulesの扱い モジュール定義 Unnamed Modules Automatic Modules name なし Jarファイル名 または、 JarファイルのMANIFEST属性で指定 requires ALL-MODULE-PATH ALL-UNNAMED ALL-SYSTEM ALL-MODULE-PATH ALL-UNNAMED ALL-SYSTEM exports *(全て公開) *(全て公開) opens *(全て許可) *(全て許可) 14
  • 15. モジュールの分類 System Modules Unnamed Modules Automatic Modules Named Modules module- info fizz module- info module- info 15 module- info ⁉ モジュール名がない! 経過措置:将来は削除される予定だが、JDK 16 でも継続される JARファイル名 モジュール名
  • 17. モジュールの分類 System Modules Unnamed Modules Automatic Modules Named Modules module- info fizz module- info module- info 17 module- info ⁉ モジュール名がない! JARファイル名 モジュール名 再掲載
  • 18. module- info Automatic Modules Unnamed Modules → Automatic Modules Unnamed Modules 18 ⁉ module- info モジュール名がない! Unnamed Modules は、 Automatic Modules を含む全てのモジュールを 参照(requires)と定義されている JARファイル名 モジュール名 module fileA { exports *; opens *; } 全てのパッケージを公開 全てのパッケージへの Deep Reflection を許可 <凡例>   :モジュール定義に従いアクセス制御される Direct Reflection Deep
 Reflection
  • 19. module- info Automatic Modules Automatic Modules → Unnamed Modules Unnamed Modules 19 Direct Reflection Deep
 Reflection module ⁉ { exports *; opens *; } 全てのパッケージを公開 全てのパッケージへの Deep Reflection を許可 ⁉ module- info Automatic Modules は、 Unnamed Modules を含む全てのモジュールを 参照(requires)と定義されている モジュール名がない! JARファイル名 モジュール名 <凡例>   :モジュール定義に従いアクセス制御される
  • 20. Unnamed Modules → Named Modules ⁉ module- info モジュール名がない! fizz module- info Named Modules Unnamed Modules は、 Named Modules を含む全てのモジュールを 参照(requires)と定義されている <凡例>   :モジュール定義に従いアクセス制御される Direct Reflection Deep
 Reflection module fizz { exports D; opens D; } モジュール定義の内容は、 モジュール毎に異なる Unnamed Modules 20
  • 21. module- info Automatic Modules Automatic Modules → Named Modules 21 JARファイル名 モジュール名 <凡例>   :モジュール定義に従いアクセス制御される fizz module- info Named Modules Direct Reflection Deep
 Reflection Automatic Modules は、 Named Modules を含む全てのモジュールを 参照(requires)と定義されている module fizz { exports D; opens D; } モジュール定義の内容は、 モジュール毎に異なる
  • 22. Named Modules → Unnamed Modules Unnamed Modules fizz module- info Named Modules 22 Direct Reflection Deep
 Reflection ⁉ <凡例>  :Named Modules のモジュール定義にて requires 指定できないので、アクセスできない module ⁉ { exports *; opens *; } Unnamed Modules は、 モジュール名がない module- info モジュール名がない! Named Modules は、 参照したいモジュールを requires で定義する必要がある
  • 23. module- info Automatic Modules Named Modules → Automatic Modules 23 JARファイル名 モジュール名 <凡例>   :モジュール定義に従いアクセス制御される fizz module- info Named Modules module fizz { exports D; opens D; requires fileA; } Named Modules は、 参照したいモジュールを requires で定義する必要がある module fileA { exports *; opens *; } 全てのパッケージを公開 全てのパッケージへの Deep Reflection を許可 Direct Reflection Deep
 Reflection
  • 24. module- info Automatic Modules Automatic Modules → System Modules 24 Direct Reflection Deep
 Reflection Automatic Modules は、 System Modules を含む全てのモジュールを 参照(requires)と定義されている JARファイル名 モジュール名 System Modules module- info module buzz { exports D; } モジュール定義の内容は、 モジュール毎に異なる <凡例>   :モジュール定義に従いアクセス制御される
  • 25. Unnamed Modules → System Modules 25 System Modules module- info Unnamed Modules ⁉ module- info モジュール名がない! module buzz { exports D; } Unnamed Modules は、 System Modules を含む全てのモジュールを 参照(requires)と定義されている Direct Reflection Deep
 Reflection ! --illegal-access=permit JDK 15 以前の挙動 ! ! <凡例>   :モジュール定義に反して常にアクセス可能(警告メッセージ) !
  • 26. モジュール定義に反して常にアクセス可能 $ java --show-version Main2.java openjdk 15.0.2 2021-01-19 OpenJDK Runtime Environment (build 15.0.2+7-27) OpenJDK 64-Bit Server VM (build 15.0.2+7-27, mixed mode, sharing) WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to method java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean) WARNING: Please consider reporting this to the maintainers of Main2 WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release 26
  • 27. Unnamed Modules → System Modules 27 System Modules module- info Unnamed Modules ⁉ module- info モジュール名がない! module buzz { exports D; } モジュール定義の内容は、 モジュール毎に異なる Unnamed Modules は、 System Modules を含む全てのモジュールを 参照(requires)と定義されている Direct Reflection Deep
 Reflection --illegal-access=deny JDK 16 以降の挙動 <凡例>   :モジュール定義に従いアクセス制御される
  • 28. モジュール定義に従いアクセス制御される $ java --show-version Main2.java openjdk 16 2021-03-16 OpenJDK Runtime Environment (build 16+35-2229) OpenJDK 64-Bit Server VM (build 16+35-2229, mixed mode, sharing) java.lang.reflect.InaccessibleObjectException: Unable to make private boolean java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean) accessible: module java.base does not "opens java.util.concurrent" to unnamed module @7f416310 at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:357) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199) at java.base/java.lang.reflect.Method.setAccessible(Method.java:193) at Main3.main(Main2.java:18) 28 RuntimeException
  • 29. JDK内部API System Modules の非公開な APIのうち、 --illegal-access=deny の影響を受ける箇所(分類)を知る!
  • 30. JDK内部API 本来、アプリケーションやライブラリで利用されることを想定していない 非公開な API であり、sun.* や com.sun.* または jdk.* パッケージなど… ここでは、以下の3つに分類する 代替APIが存在する 影響が大きい その他 30 System Modules module- info
  • 31. 代替APIが存在する JDK内部API https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool 31 JDK 8の時点で既に代替APIが提供されており、現在は削除されている 標準API JDK内部API 標準API JDK内部API
  • 32. 影響が大きい JDK内部API 重要な機能を提供しているが、現時点でも代替APIが存在しない… 経過措置:jdk.unsupported モジュールに格納されて提供されている 将来は削除される予定だが、JDK 16 でも継続される sun.misc.Signal sun.misc.SignalHandler sun.misc.Unsafe sun.reflect.Reflection::getCallerClass(int) sun.reflect.ReflectionFactory com.sun.nio.file.ExtendedCopyOption com.sun.nio.file.ExtendedOpenOption com.sun.nio.file.ExtendedWatchEventModifier com.sun.nio.file.SensitivityWatchEventModifier $ java --describe-module jdk.unsupported jdk.unsupported@16 exports com.sun.nio.file exports sun.misc exports sun.reflect requires java.base mandated opens sun.misc opens sun.reflect JDK 9 で代替APIが提供され たのでJDK 11で削除された 32
  • 33. その他 JDK内部API ① 他の分類に該当しない、sun.* や com.sun.* パッケージなどの非公開パッケージ JDK 9以降の環境では、基本的にカプセル化されている(アクセス不可) 経過措置(JDK15まで):実行時に限り、Unnamed module に対して公開 (exports) ② 全パッケージに含まれる private クラスやメソッド、フィールドなど… Deep Reflection によるアクセスを必須とする 経過措置(JDK15まで):Unnamed module に対して deep reflection を許可 (opens) 33
  • 34. JDK内部APIの分類とその影響 JEP 396(--illegal-access のパラメータを permit から deny に変更)の影響 がJDK内部APIの分類に応じて異なる JDK内部APIの分類 --illegal-access=permit --illegal-access=deny 代替APIが存在する ❌ (NoClassDefFoundError などが発生) ❌ (NoClassDefFoundError などが発生) 影響が大きい (jdk.unsupported モジュール) ✔ ✔ その他 ✔ ❌ (InaccessibleObjectException などが発生) <凡例> ✔:問題なくアクセス可能 ❌:アクセス不可(例外が発生する) 34 JDK 16 のデフォルト値
  • 36. Unnamed Module による許可されていないJDK内部APIへのアクセスに対 する挙動を、パラメータ値に応じて制御する --illegal-access のパラメータと挙動 パラメータ値 初回アクセス 2回目以降のアクセス 補足 permit △ ✔ JDK 9 ∼15のデフォルト値 warn △ △ ー debug △ (スタックトレースも出力) △ (スタックトレースも出力) ー deny ❌ ❌ JDK 16以降のデフォルト値 <凡例> ✔:問題なくアクセス可能 △:アクセス可能(但し、警告メッセージを出力) ❌:アクセス不可(例外が発生する) 36
  • 37. --illegal-access=warn の場合 $ java --show-version --illegal-access=warn Main2.java openjdk 15.0.2 2021-01-19 OpenJDK Runtime Environment (build 15.0.2+7-27) OpenJDK 64-Bit Server VM (build 15.0.2+7-27, mixed mode, sharing) WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to method java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean) WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to method java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean) WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to field com.sun.crypto.provider.BlowfishConstants.BLOWFISH_BLOCK_SIZE 37 警告メッセージ(許可されないアクセスの回数分)
  • 38. --illegal-access=debug の場合 $ java --show-version --illegal-access=debug Main2.java openjdk 15.0.2 2021-01-19 OpenJDK Runtime Environment (build 15.0.2+7-27) OpenJDK 64-Bit Server VM (build 15.0.2+7-27, mixed mode, sharing) WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to method java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean) at Main2.main(Main2.java:18) WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to method java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean) at Main2.main(Main2.java:26) WARNING: Illegal reflective access by Main2 (file:/2021/Main2.java) to field com.sun.crypto.provider.BlowfishConstants.BLOWFISH_BLOCK_SIZE at Main2.main(Main2.java:35) 38 警告メッセージとスタックトーレス(許可されないアクセスの回数分)
  • 40. JDK内部APIの分類とその影響 JEP 396(--illegal-access のパラメータを permit から deny に変更)の影響 がJDK内部APIの分類に応じて異なる JDK内部APIの分類 --illegal-access=permit --illegal-access=deny 代替APIが存在する ❌ (NoClassDefFoundError などが発生) ❌ (NoClassDefFoundError などが発生) 影響が大きい (jdk.unsupported モジュール) ✔ ✔ その他 ✔ ❌ (InaccessibleObjectException などが発生) <凡例> ✔:問題なくアクセス可能 ❌:アクセス不可(例外が発生する) 40 JDK 16 のデフォルト値 再掲載
  • 41. JDK内部APIを利用するコード例 try { sun.misc.BASE64Encoder b64 = new sun.misc.BASE64Encoder(); System.out.println(b64.encode(new byte[]{1, 2, 3})); } catch (Throwable ex) { ex.printStackTrace(); } try { Field field = sun.misc.Unsafe.class.getDeclaredField(“theUnsafe"); field.setAccessible(true); sun.misc.Unsafe unsafe = (sun.misc.Unsafe) field.get(null); } catch (Exception ex) { ex.printStackTrace(); } try { java.util.conccurent.ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); Method addWorker = ThreadPoolExecutor.class.getDeclaredMethod(“addWorker", Runnable.class, Boolean.TYPE); addWorker.setAccessible(true); addWorker.invoke(executor, null, Boolean.FALSE); } catch (Exception ex) { ex.printStackTrace(); } その他 41 Deep Reflection 代替APIが存在する 影響が大きい
  • 42. jdeps ツールで確認⁉ $ jdeps --jdk-internals --class-path test.legacy.main.jar test.legacy.main.jar -> jdk8internals test.legacy.main.jar -> jdk.unsupported test.main.Main -> sun.misc.BASE64Encoder JDK internal API (jdk8internals) test.main.Main -> sun.misc.Unsafe JDK internal API (jdk.unsupported) 警告: JDK内部APIはサポートされておらず、JDK実装専用ですが、互換性なしで 削除または変更される場合があり、アプリケーションを中断させる可能性があります。 JDK内部APIの依存性を削除するようコードを変更してください。 JDK内部APIの置換に関する最新の更新については、次を確認してください: https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool JDK内部API 修正候補 -------- ---- sun.misc.BASE64Encoder Use java.util.Base64 @since 1.8 sun.misc.Unsafe See http://openjdk.java.net/jeps/260 42
  • 43. jdeps ツールで確認する際の注意点 --jdk-internals オプションで確認できる JDK内部API の種類は限られる! JDK内部APIの分類 jdepsツールでの確認 例 代替APIが存在する ✔ sun.misc.BASE64Decoder sun.reflect.Reflection.getCallerClass 影響が大きい (jdk.unsupported モジュール) ✔ sun.misc.Unsafe com.sun.nio.file.ExtendedCopyOption その他 ❌ java.util.concurrent.ThreadPoolExecutor.addWorker java.security.MessageDigest.provider sun.security.util.SecurityConstants <凡例> ✔:確認できる ❌:確認できない 43 direct アクセスの箇所のみ検出できる
  • 44. ⁉ setAccessible(true) の箇所を特定⁉ 全てのソースコード(商用版を含む)を保持しているなら可能かもねぇ… OSSの場合でもマイナーバージョンまで完全把握していないとねぇ… System modules 以外のモジュールに対する箇所もヒットするけどねぇ… やらないより、やる価値はあるので、自作のソースコードだけでも! 44
  • 45. JDK 16 以降に向けた準備・確認 以下のように JEP 396 による影響を確認する 1.--illegal-access=debug を明示的に指定して Java VM を起動する 警告メッセージとスタックトレースを基に修正すべき箇所を確認する 2.--illegal-access=deny を明示的に指定して Java VM を起動する 例外が発生することなく、アプリケーションが正常動作することを確認する 45
  • 47. JDK 16 では、やっぱり例外発生! $ java --show-version Main3.java openjdk 16 2021-03-16 OpenJDK Runtime Environment (build 16+35-2229) OpenJDK 64-Bit Server VM (build 16+35-2229, mixed mode, sharing) java.lang.reflect.InaccessibleObjectException: Unable to make private boolean java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean) accessible: module java.base does not "opens java.util.concurrent" to unnamed module @7f416310 at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:357) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199) at java.base/java.lang.reflect.Method.setAccessible(Method.java:193) at Main3.main(Main3.java:18) 47 RuntimeException
  • 48. ソースコード修正なしに対処するには… $ java --show-version --add-opens java.base/java.util.concurrent=ALL-UNNAMED Main3.java openjdk 16 2021-03-16 OpenJDK Runtime Environment (build 16+35-2229) OpenJDK 64-Bit Server VM (build 16+35-2229, mixed mode, sharing) 48 この対処は、リスクをともなうので注意が必要である モジュール提供者の意思に反している! JDK内部APIが変更・削除された場合、失敗する!
  • 49. JDK 16 では --illegal-access は deprecated $ java --show-version --illegal-access=permit Main3.java OpenJDK 64-Bit Server VM warning: Option --illegal-access is deprecated and will be removed in a future release. openjdk 16 2021-03-16 OpenJDK Runtime Environment (build 16+35-2229) OpenJDK 64-Bit Server VM (build 16+35-2229, mixed mode, sharing) WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by Main3 (file:/2021/Main3.java) to method java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean) WARNING: Please consider reporting this to the maintainers of Main3 WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release 49 警告メッセージ(初回アクセス分のみ)
  • 51. JEP 396での変更点 従来(JDK 9 ∼ 15) --illegal-access=permit をデフォルト設定としてJava VMが起動する 今後(JDK 16以降) --illegal-access=deny をデフォルト設定としてJava VMが起動する 51 再掲載
  • 52. Unnamed Modules → System Modules 52 System Modules module- info Unnamed Modules ⁉ module- info モジュール名がない! module buzz { exports D; } モジュール定義の内容は、 モジュール毎に異なる Unnamed Modules は、 System Modules を含む全てのモジュールを 参照(requires)と定義されている Direct Reflection Deep
 Reflection --illegal-access=permit JDK 15 以前の挙動 再掲載 ! ! <凡例>   :モジュール定義に反して常にアクセス可能(警告メッセージ) ! !
  • 53. Unnamed Modules → System Modules 53 System Modules module- info Unnamed Modules ⁉ module- info モジュール名がない! module buzz { exports D; } モジュール定義の内容は、 モジュール毎に異なる Unnamed Modules は、 System Modules を含む全てのモジュールを 参照(requires)と定義されている Direct Reflection Deep
 Reflection --illegal-access=deny JDK 16 以降の挙動 <凡例>   :モジュール定義に従いアクセス制御される 再掲載
  • 54. その他 JDK内部API ① 他の分類に該当しない、sun.* や com.sun.* パッケージなどの非公開パッケージ JDK 9以降の環境では、基本的にカプセル化されている(アクセス不可) 経過措置(JDK15まで):実行時に限り、Unnamed module に対して公開 (exports) ② 全パッケージに含まれる private クラスやメソッド、フィールドなど… Deep Reflection によるアクセスを必須とする 経過措置(JDK15まで):Unnamed module に対して deep reflection を許可 (opens) 54 再掲載
  • 55. JDK 16 以降に向けた準備・確認 以下のように JEP 396 による影響を確認する 1.--illegal-access=debug を明示的に指定して Java VM を起動する 警告メッセージとスタックトレースを基に対処すべき箇所を確認する 2.--illegal-access=deny を明示的に指定して Java VM を起動する 例外が発生することなく、アプリケーションが正常動作することを確認する 55 再掲載
  • 56. --add-opens/--add-exports のリスク $ java --show-version --add-opens java.base/java.util.concurrent=ALL-UNNAMED Main2.java openjdk 16 2021-03-16 OpenJDK Runtime Environment (build 16+35-2229) OpenJDK 64-Bit Server VM (build 16+35-2229, mixed mode, sharing) 56 この対処は、リスクをともなうので注意が必要である モジュール提供者の意思に反している! JDK内部APIが変更・削除された場合、失敗する!
  • 58. 参考情報 JDK 16 - OpenJDK - https://openjdk.java.net/projects/jdk/16/ JEP 396: Strongly Encapsulate JDK Internals by Default https://openjdk.java.net/jeps/396 Relaxed Strong encapsulation - JEP 261: Module System - https://openjdk.java.net/jeps/261#Relaxed-strong-encapsulation 58
  • 59. 参考情報 JEP 260: Encapsulate Most Internal APIs https://openjdk.java.net/jeps/260 Replace uses of the JDK’s internal APIs - Java Dependency Analysis Tool - https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool Internal packages that will no longer be open by default 
 / Exported packages that will no longer be open by default https://cr.openjdk.java.net/~mr/jigsaw/jdk8-packages-denied-by-default 59
  • 60. その他 使ってみよう!JDK Flight Recorder https://www.slideshare.net/tokumasu123/jdk-flight-recorder-126001298 ログ出力を改めて考える - JDK Flight Recorder の活用 - https://www.slideshare.net/tokumasu123/jdk-flight-recorder-155345312 JFR Event Streaming によるAP監視 - JDK Flight Recorder の活用 - https://www.slideshare.net/tokumasu123/jfr-event-streamingap-jdk-flight-recorder 60
  • 61. END “JDK 16 で導入された JEP 396 にご注意” JEP 396 : Strongly Encapsulate JDK Internals by Default