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

PDF
JVMのGCアルゴリズムとチューニング
PDF
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
PDF
Unified JVM Logging
PDF
最適なOpenJDKディストリビューションの選び方 #codetokyo19B3 #ccc_l5
PDF
これからのJDK 何を選ぶ?どう選ぶ? (v1.2) in 熊本
PDF
Dockerfileを改善するためのBest Practice 2019年版
PDF
負荷テストを行う際に知っておきたいこと 初心者編
PDF
統計情報のリセットによるautovacuumへの影響について(第39回PostgreSQLアンカンファレンス@オンライン 発表資料)
JVMのGCアルゴリズムとチューニング
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
Unified JVM Logging
最適なOpenJDKディストリビューションの選び方 #codetokyo19B3 #ccc_l5
これからのJDK 何を選ぶ?どう選ぶ? (v1.2) in 熊本
Dockerfileを改善するためのBest Practice 2019年版
負荷テストを行う際に知っておきたいこと 初心者編
統計情報のリセットによるautovacuumへの影響について(第39回PostgreSQLアンカンファレンス@オンライン 発表資料)

What's hot (20)

PPTX
Metaspace
PPTX
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...
PDF
JDKの選択肢とサーバーサイドでの選び方
PDF
Java EE から Quarkus による開発への移行について
PPTX
Java 17直前!オレ流OpenJDK「の」開発環境(Open Source Conference 2021 Online/Kyoto 発表資料)
PPTX
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
PDF
Mavenの真実とウソ
PDF
Apache Kafkaって本当に大丈夫?~故障検証のオーバービューと興味深い挙動の紹介~
PPTX
GraalVMのJavaネイティブビルド機能でどの程度起動が速くなるのか?~サーバレス基盤上での評価~ / How fast does GraalVM's...
PDF
Grafana LokiではじめるKubernetesロギングハンズオン(NTT Tech Conference #4 ハンズオン資料)
PPTX
その Pod 突然落ちても大丈夫ですか!?(OCHaCafe5 #5 実験!カオスエンジニアリング 発表資料)
PDF
わかる!metadata.managedFields / Kubernetes Meetup Tokyo 48
PDF
バイトコードって言葉をよく目にするけど一体何なんだろう?(JJUG CCC 2022 Spring 発表資料)
PDF
Kubernetesのしくみ やさしく学ぶ 内部構造とアーキテクチャー
PDF
CPUから見たG1GC
ODP
Guide To AGPL
PDF
Linux女子部 systemd徹底入門
PPTX
コンテナネットワーキング(CNI)最前線
PDF
Docker Compose 徹底解説
Metaspace
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...
JDKの選択肢とサーバーサイドでの選び方
Java EE から Quarkus による開発への移行について
Java 17直前!オレ流OpenJDK「の」開発環境(Open Source Conference 2021 Online/Kyoto 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
Mavenの真実とウソ
Apache Kafkaって本当に大丈夫?~故障検証のオーバービューと興味深い挙動の紹介~
GraalVMのJavaネイティブビルド機能でどの程度起動が速くなるのか?~サーバレス基盤上での評価~ / How fast does GraalVM's...
Grafana LokiではじめるKubernetesロギングハンズオン(NTT Tech Conference #4 ハンズオン資料)
その Pod 突然落ちても大丈夫ですか!?(OCHaCafe5 #5 実験!カオスエンジニアリング 発表資料)
わかる!metadata.managedFields / Kubernetes Meetup Tokyo 48
バイトコードって言葉をよく目にするけど一体何なんだろう?(JJUG CCC 2022 Spring 発表資料)
Kubernetesのしくみ やさしく学ぶ 内部構造とアーキテクチャー
CPUから見たG1GC
Guide To AGPL
Linux女子部 systemd徹底入門
コンテナネットワーキング(CNI)最前線
Docker Compose 徹底解説
Ad

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

PDF
JavaOne 2015 JDK Update (Jigsaw) #j1jp
PDF
OpenJDKのコミッタってどんなことしたらなったの?解決してきた技術課題の事例から見えてくる必要な知識と技術(JJUG CCC 2023 Spring)
PDF
今年はJava進化の年!今知っておくべき新しいJava
PDF
10のJava9で変わるJava8の嫌なとこ!
PDF
Prepare for Java 9 #jjug
PDF
Head toward Java 16 (Night Seminar Edition)
PDF
JavaOne 2016 Java SE Feedback #jjug #j1jp
PDF
Javaヂカラ #Java最新動向 -Java 11 の新機能やOracle Code One 2018 発の最新技術トレンドを一気にキャッチアップ-
PPTX
Java 18で入ったJVM関連の(やや細かめな)改善(JJUGナイトセミナー「Java 18 リリース記念イベント」発表資料)
PPTX
Let's Start Contributing to OpenJDK from Today!(Oracle Groundbreakers APAC Vi...
PDF
Var handles jjug_ccc_spring_2018
PDF
Head toward Java 15 and Java 16
KEY
関ジャバ JavaOne Tokyo 2012報告会
PDF
Valhalla Update JJUG CCC Spring 2019
PPTX
Cve 2013-0422
PDF
ClassLoader Leak Patterns
PPTX
Jdk9で変更になる(かも知れない)jvmオプションの標準設定
PPT
PDF
JDK Mission Control: Where We Are, Where We Are Going [Groundbreakers APAC 20...
ODP
これから Haskell を書くにあたって
JavaOne 2015 JDK Update (Jigsaw) #j1jp
OpenJDKのコミッタってどんなことしたらなったの?解決してきた技術課題の事例から見えてくる必要な知識と技術(JJUG CCC 2023 Spring)
今年はJava進化の年!今知っておくべき新しいJava
10のJava9で変わるJava8の嫌なとこ!
Prepare for Java 9 #jjug
Head toward Java 16 (Night Seminar Edition)
JavaOne 2016 Java SE Feedback #jjug #j1jp
Javaヂカラ #Java最新動向 -Java 11 の新機能やOracle Code One 2018 発の最新技術トレンドを一気にキャッチアップ-
Java 18で入ったJVM関連の(やや細かめな)改善(JJUGナイトセミナー「Java 18 リリース記念イベント」発表資料)
Let's Start Contributing to OpenJDK from Today!(Oracle Groundbreakers APAC Vi...
Var handles jjug_ccc_spring_2018
Head toward Java 15 and Java 16
関ジャバ JavaOne Tokyo 2012報告会
Valhalla Update JJUG CCC Spring 2019
Cve 2013-0422
ClassLoader Leak Patterns
Jdk9で変更になる(かも知れない)jvmオプションの標準設定
JDK Mission Control: Where We Are, Where We Are Going [Groundbreakers APAC 20...
これから Haskell を書くにあたって
Ad

Recently uploaded (8)

PPTX
生成AIとモデルベース開発:実はとても相性が良いことを説明します。まあそうだろうなと思われる方はご覧ください。
PPTX
Cosense - 整えずして完全勝利!Cosenseが他のwikiツールと違う理由
PDF
R-SCoRe: Revisiting Scene Coordinate Regression for Robust Large-Scale Visual...
PPTX
Vibe Codingを触って感じた現実について.pptx .
PDF
20250826_Devinで切り拓く沖縄ITの未来_AI駆動開発勉強会 沖縄支部 第2回
PDF
Geminiの出力崩壊 本レポートは、Googleの大規模言語モデル「Gemini 2.5」が、特定の画像と短文入力に対して、誤った地名を推定し、最終的に...
PDF
20250823_IoTLT_vol126_kitazaki_v1___.pdf
PDF
Yamaha DT200WR Real Enduro ENGINE CYLINDER TRANSMISSION
生成AIとモデルベース開発:実はとても相性が良いことを説明します。まあそうだろうなと思われる方はご覧ください。
Cosense - 整えずして完全勝利!Cosenseが他のwikiツールと違う理由
R-SCoRe: Revisiting Scene Coordinate Regression for Robust Large-Scale Visual...
Vibe Codingを触って感じた現実について.pptx .
20250826_Devinで切り拓く沖縄ITの未来_AI駆動開発勉強会 沖縄支部 第2回
Geminiの出力崩壊 本レポートは、Googleの大規模言語モデル「Gemini 2.5」が、特定の画像と短文入力に対して、誤った地名を推定し、最終的に...
20250823_IoTLT_vol126_kitazaki_v1___.pdf
Yamaha DT200WR Real Enduro ENGINE CYLINDER TRANSMISSION

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