Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Java8〜16におけるバイトコード生成の変化 / Changes of Bytecode Generation from Java 8 to 16

Java8〜16におけるバイトコード生成の変化 / Changes of Bytecode Generation from Java 8 to 16

JJUG CCC 2021 Springで発表したものです。Java8〜16においてOpenJDKが生成するバイトコードにどのような変化があったのか、静的解析ツール開発者の視点から紹介します。
https://fortee.jp/jjug-ccc-2021-spring/proposal/288ac3a0-9683-4ed5-89e9-fbe1a61ca5c8

4ccf6c02807d06f043a71435c48ce86a?s=128

Kengo TODA

May 23, 2021
Tweet

Transcript

  1. Java8〜16における バイトコード生成の変化 2021/May/23, Kengo TODA

  2. whoami • 15歳からプログラム書いてる35歳 • Java, TypeScript, Gradle, Sphinx, development workflowなどが好き

    • Javaバイトコードの静的解析ツールSpotBugsの開発メンバー 2
  3. このセッションの狙い • 静的解析ツール開発者の目線から、バイトコード生成の変化についてJEPにあるも のから細かいものまで紹介 • 明日使えないムダ知識 • OpenJDKのissueやcommitを掘るときに参考できるかもしれない 3

  4. 前提とする知識 • javac, バイトコード, JEPなどの単語 • Java 8〜16であった変化のざっくりした理解 4

  5. Java 9 警戒していたほど 大変ではなかった jigsaw対応 • JEP 220: Modular Run-Time

    Images • JEP 238: Multi-Release JAR Files • JEP 213: Milling Project Coin 5
  6. 2017年のJJUG CCC Springで…… 6 「Jigsaw対応こわい、やりたくない」と LTで言いまし た。 IBMとRed Hatに勝手に期待していた私。

  7. jigsawはこわくなかった! 蓋を開けてみればJarの形式が変わるわけでもCLASSPATHという概念がなくなるわけ でもなく。静的にファイルを読む静的解析ツールにとっては破壊的変更とはなりませんで した。 JEP 220: Modular Run-Time Imagesだけは例外ですが、rt.jarがなくなっただけなの で解析ロジックには変更無く、初期化処理がちょっと変わっただけ。

    7
  8. MRJAR……対応する? Multi-Release JAR Filesのこと。異なるJavaバージョン向けの.classファイルを、ひとつ の.jarにまとめられる仕様。例えば「アノテーションが修飾する対象にMODULEを加えた いけど、MODULEの無いJava8もサポートしたい」のようなケースで利用できる。 便利ではあるがjavadocとsourcesは未対応なので、濫用は混乱のもと。 またバグの再現を難しくする可能性もあるので要注意。 SpotBugsでは未対応。解析のコンテキストがひとつ増える=実行時引数を増やす対応 が必要か。

    8
  9. $closeResourceメソッドの爆誕 Milling Project Coinでtry-with-resources構文が改善された。 表面的な改善点は桜庭さんの記事を参照。 バイトコード的には不要なnullチェックを排除するとともに、処理をメソッドに切り出して重 複を抑える工夫がされた。これにより「AutoCloseableインスタンスがCloseされたかどうか」 の確認に複数メソッドをまたぐ必要性が生じた。 9

  10. これが$closeResourceメソッドだ! 10

  11. Java 11 JLSに沿うように実装を 修正したら、様々なツールが 壊れた回 11 • A change around

    try-with-resources • JEP 181: Nest-Based Access Control • JEP 336: Deprecate the Pack200 Tools and API
  12. Dead code in $closeResource()? Java 9で導入された手法にもデッドコードがあるということでより効率的な方法をJava 11 b07で導入したところ、JaCoCoとSpotBugsで誤作動が発生。JaCoCoは修正済みだが SpotBugsは4.2.2時点で一部のみ対応。 SpotBugsが誤検知した内容は「nullじゃないとわかっているローカル変数のnullチェック

    をしている」。Eclipseのコンパイラ(ecj)では問題なし。 バイトコードを見てみると、確かにインスタンスのメソッドを利用したあとで、そのインスタン スがnullかどうか確認している。javacのバグでは? 12
  13. SpotBugsにはOpenJDKのコミッタがいませんが、Issueで議論していた人の中にいた Ismael Juma氏がcompiler-devメーリスで代わりに聞いてくれました。 spotBugs and JDK-8194978: Javac produces dead code

    for try-with-resource その結果、驚愕の事実が判明。 OpenJDKコミッタじゃなくてもなんとかなるよ 13
  14. 「nullチェックしろとJLSに書いてあるよ」v7から。 14

  15. try-with-resources構文のバイトコード変化まとめ 15 • Java 7でtry-with-resources構文が導入される。 • Java 9で、デッドコードを減らしコードの重複を省く目的で、 $closeResource() メソッ

    ドが自動的に生成されるようになる。メソッドをまたいだ解析が必要になる。 • Java 11 b7で、デッドコードを減らす目的で $closeResource() メソッドが削除され る。無意味なnullチェックコードが含まれているが、JLSに必要と記載されているので 問題ない。
  16. そのほかのJava 11の変更 • JEP 181: Nest-Based Access Controlについてはじゅくちょー氏のブログに詳しい ので割愛。SpotBugsでの修正はこちら。 •

    JEP 336: Deprecate the Pack200 Tools and APIはバイトコードの変化ではない が、JaCoCoに影響を及ぼしたということでここに載せておきます。 16
  17. Java 14 OpenJDKとコミュニティが協調した 模範的な事例 • JEP 305: Pattern Matching for

    instanceof (Preview) • JEP 359: Records (Preview) 17
  18. Pattern Matching:ことの発端 Number number = 1f; if (number instanceof Float

    f) { System.out.println( "number is a float: " + f ); } 一見問題なさそうなコードだが、 SpotBugsではロー カル変数を自分自身と比較している という警告が出 る。 JaCoCoではカバレッジの異常として報告される。 Eclipseのコンパイラ(ecj)では問題なし。 18
  19. どのようなバイトコードか 7: aload_2 8: instanceof #8 // class java/lang/Float 11:

    ifeq ... 14: aload_2 15: checkcast #8 // class java/lang/Float 18: dup 19: astore_1 20: aload_2 21: checkcast #8 // class java/lang/Float 24: if_acmpne ... jdk-14.0.2+12同梱のjavacで確認。 確かにaload_2で取り出した同じローカル変数を if_acmpneで比較している。 19
  20. Evgeny Mandrikov氏がProject Amberのメーリスで指摘 20

  21. Rémi Forax氏がjavacのバグとして登録するよう返信 21

  22. Evgeny Mandrikov氏がバグとして登録 22

  23. Jan Lahoda氏が修正、Java 15のGAに入る 23

  24. Java15で変化した内容 11: ifeq ... 14: aload_2 15: checkcast #8 //

    class java/lang/Float - 18: dup - 19: astore_1 - 20: aload_2 - 21: checkcast #8 // class java/lang/Float - 24: if_acmpne ... + 18: astore_1 余計な比較が無くなり、非常にシンプルなバイト コードに。🎉 素早いリリースサイクルとオープンな開発体制がプ ラスに働いた模範的な事例ではないでしょうか? 24
  25. Recordのequals()がベストプラクティスに沿ってない? Java14もうひとつの誤検知は、Recordクラスのequals()実装がベストプラクティスに沿って ないというもの。 equals()といえばIDEによる自動生成もこなれている領域。 javacが妙なコードを生成するとは思えないのだが……? 25

  26. なんじゃこりゃ 26

  27. 困ったときのOracleさんも、目的については説明せず 27

  28. なぜ動的生成なのか、調査中 愚直な実装より利点が多いのでinvoke dynamicを利用しているはず。.classファイルサイ ズ削減とか、実行パフォーマンスとか? • Switch pattern matching の実装に invokedynamic

    が良いという話はあった 情報お待ちしておりますm(_ _)m 28
  29. Microbenchmark in my local (https://git.io/JODNQ) IntelliJ IDEAの生成したhashCode()実装と、 record標準のinvokedynamicを利用した hashCode()実装との単純な性能比較。横軸は Recordに含まれるフィールドの数と種類。縦軸は秒

    間実行回数で、高い方が速い。 invokedynamicを利用したhashCode()実装の方が 若干遅い。primitiveだと特に差が顕著。 今は遅いとはいえ、ObjectMethodsクラスが生成す るロジックはJavaの成長とともに成長するので、強 い理由がなければinvokedynamicを利用した hashCode()実装で良いのでは。 29
  30. 注意:配列をRecordに使わないこと 配列使うとmutableになるから使うべきではない、というのに加えて。生成される hashCode() と equals(Object), toString() が配列を考慮しない実装になっているため、 JavaのRecordでは配列を使わないほうが良いようです。 詳細はブログ記事参照。 30

  31. Java 16 解析ツールにとって 肩身が狭い世の中 • JEP 396: Strongly Encapsulate JDK

    Internals by Default 31
  32. JDK内部の実装を積極的にカプセル化していく予定 JEP 396の影響で、Google Errorproneが依存しているcom.sun.tools.javacパッケージ などが標準でアクセスできなくなった。このためErrorproneのビルド時に--illegal-access オプションを追加する対応を行った模様。 続くJEP 403でもJDK内部実装は積極的にカプセル化され、将来的に は--illegal-accessオプションも削除予定。API標準化が進むことが期待されます。 32

  33. まとめ Java 9, 11, 14, 16にわたって 様々な変更を見てきた • OpenJDK is

    Open • 14に問題があったら15で直せば いい • デッドコード削除やIndy活用など 33
  34. おわりに:オープンソースしようぜ! • 今回の事例のように、あなたの報告したIssueが開発の手助けになるかも • (OpenJDKに限らず)コミュニティの開発者は新しい人に協力的な事が多い • オープンソースガイドラインなども参考に 34

  35. ご清聴ありがとうございました 35

  36. 付録:Opcodeは増えたか? 増減なし • Java8のJVMSとJava16のJVMSでは <h3 class="title"> が増えてない 36

  37. 付録:Attributesは増えたか? 増えた • JVMS 4.7.25. The Module Attribute • JVMS

    4.7.26. The ModulePackages Attribute • JVMS 4.7.27. The ModuleMainClass Attribute • JVMS 4.7.28. The NestHost Attribute • JVMS 4.7.29. The NestMembers Attribute • JVMS 4.7.30. The Record Attribute 37