Recommended Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
フックを使ったPostgreSQLの拡張機能を作ってみよう!(第33回PostgreSQLアンカンファレンス@オンライン 発表資料)
BuildKitによる高速でセキュアなイメージビルド
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)
【第26回Elasticsearch勉強会】Logstashとともに振り返る、やっちまった事例ごった煮
Elasticsearchを使うときの注意点 公開用スライド
仕様起因の手戻りを減らして開発効率アップを目指すチャレンジ 【DeNA TechCon 2020 ライブ配信】
モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―
CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編
あなたのScalaを爆速にする7つの方法(日本語版)
組み込み関数(intrinsic)によるSIMD入門
スマホゲームのチート手法とその対策 [DeNA TechCon 2019]
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
ストリーム処理プラットフォームにおけるKafka導入事例 #kafkajp
JJUG CCC 2015 Spring 「新人エンジニア奮闘記 - Javaって何?からwebサービスを公開するまで -」発表スライド
More Related Content Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
フックを使ったPostgreSQLの拡張機能を作ってみよう!(第33回PostgreSQLアンカンファレンス@オンライン 発表資料)
BuildKitによる高速でセキュアなイメージビルド
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)
What's hot (20) 【第26回Elasticsearch勉強会】Logstashとともに振り返る、やっちまった事例ごった煮
Elasticsearchを使うときの注意点 公開用スライド
仕様起因の手戻りを減らして開発効率アップを目指すチャレンジ 【DeNA TechCon 2020 ライブ配信】
モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―
CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編
あなたのScalaを爆速にする7つの方法(日本語版)
組み込み関数(intrinsic)によるSIMD入門
スマホゲームのチート手法とその対策 [DeNA TechCon 2019]
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
ストリーム処理プラットフォームにおけるKafka導入事例 #kafkajp
Viewers also liked (20)
JJUG CCC 2015 Spring 「新人エンジニア奮闘記 - Javaって何?からwebサービスを公開するまで -」発表スライド
Javaはどのように動くのか~スライドでわかるJVMの仕組み
情報編集 (web) 第4回:HTML入門 3 情報を整理する - リスト、テーブル
Google検索だけで満足しない、一歩先をいく収集・整理術(1day)
Java の Collection 関連について整理してみました
201412ことばの理解とワーキングメモリ:基本概念の整理(公開)
Similar to Javaバイトコード入門 (8) More from Kota Mizushima (20) ドワンゴにおける新卒エンジニア向けScala研修について
Scala Daysに行ってみて - あるいはスイス旅行記 -
Scala Performance Tuning Tips
About Capabilities for Uniqueness and Borrowing
Scala Macros makes it easy to provide useful libraries
Javaバイトコード入門2. 自己紹介とか Twitter: @kmizu はてな : id:kmizushima github: http://github.com/kmizu/ 大学院生 構文解析の研究とかやってます 特に Packrat Parsing Scala 好き Scala の布教活動をあちこちでやったり JVM 好き JVM 上で動作する言語処理系 Onion を開発 3. Agenda プログラミング言語としての Java バイトコード マシンモデル 型システム 命令セット クラスファイルベリファイア ベリファイアがはじく操作の例 簡単なプログラムを javap で逆アセンブルする 役に立つかもしれない javap のオプション解説 クラスファイル仕様については省略 時間が足りないので… 5. Hello, World!を逆アセンブルする public class Hello { public static void main(String[] args){ System.out.println("Hello, World!"); } } ↓ public static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String Hello, World! 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return 6. Hello, World!を読む 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 0: バイトコード配列の 0 番目を意味している getstatic: オペコードのニーモニック java/lang/System.out: System.out の完全限定名 Ljava/io/PrintStream;: System.out の型 System.out をスタックにロード 3: ldc #3; //String Hello, World! //←1: ではなく 3: である点に注意 文字列定数 "Hello, World!" をスタックにロード 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V PrintStream のメソッド println を呼び出す 8: return メソッドから return する 7. スタックマシン 演算対象( オペランド )がスタックに置かれる ←->レジスタマシン オペランドが置かれるスタック= オペランドスタック おおざっぱに言って、以下の繰り返し 1. 命令( オペコード )を取り出す 2. オペランドスタックから値をポップ 3. 命令を実行 4. 実行結果をオペランドスタックにプッシュ 5. 1.に戻る 8. スタックマシンの動作イメージ var pc: int var ops: byte[] ... while(pc < ops.length) { switch(ops[pc]) { case ADD: r = pop; l = pop; push(l + r); case SUB: r = pop; l = pop; push(l - r); case DIV: ... } pc++; } 9. 型システム プリミティブ型 byte, short, int, long, char float, double void boolean 参照型 配列型 クラス型 インタフェース型 だいたい Java と同じだけど細かい所が違う 10. 実際の型と計算上の型とカテゴリ 演算命令が直接サポートしていない型が存在 byte, short, char, boolean int 型の値として計算される カテゴリ おおざっぱに言うと、オペランドスタック上における値のサイズ boolean, byte, char, short, int, float, 参照型は 1 long, double は 2 命令のオペランドは特定のカテゴリでないと受け付けないものがある 11. 実際の型と計算上の型とカテゴリ 2 double double 2 long long 1 returnAddress returnAddress 1 reference reference 1 float float 1 int int 1 int short 1 int char 1 int byte 1 int boolean カテゴリ 計算上の型 実際の型 12. 命令の分類 大体 Java 仮想マシン仕様第 2 版に沿ってるが、一部独自に分類 ロード / ストア命令 算術命令 型変換命令 配列関係の命令 オブジェクトの生成・操作 制御命令 例外のスロー 同期化命令 メソッド呼び出し関係の命令 使われなくなった命令 13. ロード / ストア命令 (1) – 定数をオペランドスタックにロードする命令 bipush … byte の即値をプッシュ sipush … short の即値をプッシュ iconst_<i> … -1,0,...,5 をプッシュ aconst_null … null をプッシュ ldc, ldc_w, ldc2_w 文字列定数 , 整数 , 浮動小数点数を 実行時コンスタントプール からプッシュする など 14. bipush byte の即値をプッシュ フォーマット: <0x10, byte> オペランドスタック: ... ⇒ ..., value byte の即値が int の value へと符号拡張され、プッシュされる 15. ldc 実行時コンスタントプールから値をプッシュする フォーマット: <0x18, index> index が示すエントリは、 int 型または float 型の実行時定数か、文字列リテラルへのシンボル参照でなければならない オペランドスタック: ... ⇒ ..., value エントリが int 型または float 型の実行時定数の場合、該当する定数が int 型あるいは float 型としてプッシュされる エントリが文字列リテラルへのシンボル参照の場合、そのインスタンスへの参照がプッシュされる 16. ロード / ストア命令 (2) – ローカル変数の値をオペランドスタックにロードする iload … ローカル変数から int をロード iload_<n> … ローカル変数から int をロード n = 0 ~ 3 fload … ローカル変数から float をロード fload_<n> … ローカル変数から float をロード n = 0 ~ 3 aload … ローカル変数から参照型の値をロード など 17. iload ローカル変数から int をロードする フォーマット: <0x15,index> オペランドスタック: ..., ⇒ ..., value index 番目のローカル変数の値がスタックにプッシュされる index 番目のローカル変数には int 型の値が保持されていなければならない 18. fload ローカル変数から float をロードする フォーマット: <0x17,index> オペランドスタック: ..., ⇒ ..., value index 番目のローカル変数の値がスタックにプッシュされる index 番目のローカル変数には float 型の値が保持されていなければならない 19. ロード / ストア命令 (3) – 値をローカル変数にストアする istore … ローカル変数に int をストア istore_<n> … ローカル変数から int をロード n = 0 ~ 3 fstore … ローカル変数に float をストア fstore_<n> … ローカルに float をストア n = 0 ~ 3 astore … ローカル変数に参照型の値をストア など 20. istore ローカル変数に int をストアする フォーマット: <0x36,index> オペランドスタック: ..., value ⇒ ..., スタックトップの値が、 index 番目のローカル変数にストアされる スタックトップの値は int 型 でなければならない 21. fstore ローカル変数に float をストアする フォーマット: <0x38,index> オペランドスタック: ..., value ⇒ ..., スタックトップの値が、 index 番目のローカル変数にストアされる スタックトップの値は float 型 でなければならない 22. 算術命令 iadd, ladd, fadd, dadd … 加算 isub, lsub, fsub, dsub … 減算 imul, lmul, fmul, dmul … 乗算 idiv, ldiv, fdiv, ddiv … 除算 irem, lrem, frem, drem … 剰余 ineg, lneg, fneg, dneg … 符号反転 など 23. iadd int の加算を行う フォーマット: <0x60> オペランドスタック: ..., value1, value2 ⇒ ..., result value1 + value2 の結果がスタックにプッシュされる value1 と value2 は int 型 でなければならない 24. lsub long の加算を行う フォーマット: <0x65> オペランドスタック: ..., value1, value2 ⇒ ..., result value1 - value2 の結果がスタックにプッシュされる value1 と value2 は long 型 でなければならない 25. 型変換命令 ワイドニング数値変換命令 i2l … int から long への型変換を行う i2f … int から float への型変換を行う … ナローイング数値変換命令 i2b … int から byte への変換を行う i2c … int から char への変換を行う … 26. i2l int を long に変換する フォーマット: <0x85> オペランドスタック: ..., value ⇒ ..., result value が long へと符号拡張された結果がプッシュされる value は int 型 でなければならない 27. i2b int を byte に変換する フォーマット: <0x91> オペランドスタック: ..., value ⇒ ..., result value が byte へ切り捨てられ、 int へと符号拡張された結果がプッシュされる value は int 型 でなければならない 28. 配列関係の命令 配列を生成する命令 newarray … 1 次元配列を生成する … 配列の要素をロードする命令 baload … byte 型の配列要素をロードする … 配列の要素に値をストアする命令 bastore … byte 型の配列要素に値をストアする … arraylength … 配列の長さを取得する 29. newarray 配列を生成する フォーマット: <0xbc,atype> atype は生成する配列の型 ( プリミティブ型 ) を表したコード 4,5,6,7,8,9,10,11 のいずれか オペランドスタック: ..., count ⇒ ..., objectref count は int 型でなければならない 要素型が atype で長さが count の配列が生成されて、配列への参照がプッシュされる 31. オブジェクトの生成・操作 new … オブジェクトを生成 getfield … フィールドを取得 putfield … フィールドに値をストア getstatic … static フィールドを取得 putstatic … static フィールドに値をストア instanceof … Java の instanceof 相当 checkcast … 参照型のキャストを行う 32. new オブジェクトを生成する フォーマット: <0xbb,indexbyte1,indexbyte2> indexbyte はクラス型へのシンボル参照でなければならない オペランドスタック: ..., ⇒ ..., objectref 指定された型のオブジェクトが生成されて、スタックにプッシュされる 33. getstatic static フィールドを取得する フォーマット: <0xb2,indexbyte1,indexbyte2> indexbyte は該当するフィールドへのシンボル参照でなければならない オペランドスタック: ..., ⇒ ..., value static フィールドの値がスタックにプッシュされる 37. 制御命令 条件分岐命令 ifeq, iflt, ifne, ifgt, ifge, ... 複合条件分岐命令 tableswitch, lookupswitch switch 文に対応 無条件分岐命令 goto, goto_w, jsr, jsr_w, ret 39. ifeq スタックトップと 0 が等しい場合に該当アドレスにジャンプする 成功しない場合、次の命令があるアドレスから実行が再開 フォーマット: <0x99,branchbyte1,branchbyte2> branchbyte(1|2) から、分岐オフセットが生成される オペランドスタック: ..., value ⇒ ... 41. athrow 例外を throw する 適合するハンドラが見つかるまでメソッドを巻き戻る throw された例外 フォーマット: <0xbf> オペランドスタック: ..., objectref ⇒ objectref ハンドラにジャンプする際に、現在のオペランドスタックがクリアされる ハンドラにジャンプする際に、 throw された例外オブジェクトへの参照がプッシュされる 42. 同期化命令 要は synchronized 文を実装するための命令 monitorenter … モニタに入る monitorexit … モニタから抜ける monitor(enter/exit) を対応させるのはコンパイラ (javac など ) の責任 ベリファイアはチェックしない 例外や break/continue などで synchronized を抜けるときにもちゃんと対応する箇所で monitorexit を生成してあげないといけない… 43. メソッド呼び出し関係の命令 invokestatic … static メソッド呼び出し invokespecial … コンストラクタ ( 等 ) 呼び出し invokevirtual … インスタンスメソッド呼び出し invokeinterface … インタフェースのメソッド呼び出し ireturn, lreturn, freturn, dreturn, areturn, return … メソッドからの return 44. invokestatic static メソッドを呼び出す フォーマット: <0xb8,indexbyte1,indexbyte2> indexbyte は、メソッドへのシンボル参照がある実行時コンスタントプールへのインデックス オペランドスタック: ..., arg1, arg2, ..., arg n ⇒ ... arg1, arg2, ... が引数 メソッドの返り値の型に応じて結果がスタックにプッシュされる 45. invokevirtual インスタンスメソッドを呼び出す フォーマット: <0xb6,indexbyte1,indexbyte2> indexbyte は、メソッドへのシンボル参照がある実行時コンスタントプールへのインデックス オペランドスタック: ..., objectref, arg1, arg2, ..., arg n ⇒ ... objectref はオブジェクトへの参照 arg1, arg2, ... が引数 メソッドの返り値の型に応じて結果がスタックにプッシュされる interface 型の参照に対しては使えない 点に注意 invokeinterface を使う 46. 使われなくなった命令 jsr, ret … 元々は finally の実装に使われていた JDK 1.4.2 から使われなくなった ベリファイア絡みで不具合があることが判明したため http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4381996 finally は code duplication によって実装されるように変更 47. クラスファイルベリファイア クラスファイル (.class) のフォーマットが Java 仮想マシン仕様に従っているかどうかを 該当クラスの初期化より前に チェック たとえば: 同じ pc におけるオペランド・スタックのサイズは常に一定でなければならない オペランド・スタックのオーバーフローやアンダーフローは原理的に発生しない 存在しないローカル変数に対するアクセスは無い 未初期化のローカル変数に対するアクセスは無い 48. jasmin 用のプログラム jasmin: Java バイトコードアセンブラ >java Overflow Exception in thread "main" java.lang.VerifyError : (class: Overflow, method: main signature: ([Ljava/lang/String;)V) Inconsistent stack height 1 != 0 ベリファイアがはねるプログラム (1) – スタックオーバーフロー .class public Overflow .super java/lang/Object .method public static main([Ljava/lang/String;)V .limit stack 2 LABEL: ldc "Hello, World!" goto LABEL return .end method 49. >java Uninitialized Exception in thread "main" java.lang.VerifyError : (class: Uninitialized, method: main signature: ([L java/lang/String;)V) Accessing value from uninitialized register 1 ベリファイアがはねるプログラム (2) – 未初期化ローカル変数へのアクセス .class public Uninitialized .super java/lang/Object .method public static main([Ljava/lang/String;)V .limit stack 2 .limit locals 3 iload_1 return .end method 50. >java Incompatible Exception in thread "main" java.lang.VerifyError : (class: Incompatible, method: main signature: ([Lj ava/lang/String;)V) Expecting to find integer on stack ベリファイアがはねるプログラム (3) - 型エラー .class public Incompatible .super java/lang/Object .method public static main([Ljava/lang/String;)V .limit stack 2 ldc 3.5 ldc 4.0 iadd return .end method 51. >java PopLong Exception in thread "main" java.lang.VerifyError : (class: PopLong, method: main signature: ([Ljava/lang/String;)V) Attempt to split long or double on the stack ベリファイアがはねるプログラム (4) - カテゴリ間違い .class public PopLong .super java/lang/Object .method public static main([Ljava/lang/String;)V .limit stack 3 invokestatic java/lang/System/currentTimeMillis()J pop ; pop2 なら OK . pop はカテゴリ 1 の値しか pop できないが long はカテゴリ 2 return .end method 52. 練習問題 ↓ のコンパイル結果を逆アセンブルしたものを読んでみる ',' で区切られた項目 ( 整数 ) の合計値を計算 import java.util.Scanner; public class CalcSum { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while(scanner.hasNext()) { int result = 0; String[] line = scanner.nextLine().split(","); for(String element:line) result += Integer.parseInt(element); System.out.println("result:" + result); } } } 53. 練習問題 逆アセンブル (javap –c CalcSum) した結果 public class CalcSum { ↓ Compiled from "CalcSum.java" public class CalcSum extends java.lang.Object{ public CalcSum(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return 54. 練習問題 逆アセンブル (javap –c CalcSum) した結果 public static void main(String[] args) { Scanner scanner = new Scanner(System.in); ↓ public static void main(java.lang.String[]); Code: 0: new #2; //class java/util/Scanner 3: dup 4: getstatic #3; //Field java/lang/System.in:Ljava/io/InputStream; 7: invokespecial #4; //Method java/util/Scanner."<init>":(Ljava/io/InputStream;)V 10: astore_1 55. 練習問題 逆アセンブル (javap –c CalcSum) した結果 while(scanner.hasNext()) { ↓ 11: aload_1 12: invokevirtual #5; //Method java/util/Scanner.hasNext:()Z 15: ifeq 97 56. 練習問題 逆アセンブル (javap –c CalcSum) した結果 int result = 0; String[] line = scanner.nextLine().split(","); ↓ 18: iconst_0 19: istore_2 20: aload_1 21: invokevirtual #6; //Method java/util/Scanner.nextLine:()Ljava/lang/String; 24: ldc #7; //String , 26: invokevirtual #8; //Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String; 29: astore_3 30: aload_3 31: astore 4 57. 練習問題 逆アセンブル (javap –c CalcSum) した結果 for(String element:line) result += Integer.parseInt(element); ↓ 33: aload 4 35: arraylength 36: istore 5 38: iconst_0 39: istore 6 41: iload 6 43: iload 5 45: if_icmpge 69 48: aload 4 50: iload 6 52: aaload 53: astore 7 55: iload_2 56: aload 7 58: invokestatic #9; //Method java/lang/Integer.parseInt:(Ljava/lang/String;)I 61: iadd 62: istore_2 63: iinc 6, 1 66: goto 41 58. 練習問題 逆アセンブル (javap –c CalcSum) した結果 System.out.println("result:" + result); } } } ↓ 69: getstatic #10; //Field java/lang/System.out:Ljava/io/PrintStream; 72: new #11; //class java/lang/StringBuilder 75: dup 76: invokespecial #12; //Method java/lang/StringBuilder."<init>":()V 79: ldc #13; //String result: 81: invokevirtual #14; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 84: iload_2 85: invokevirtual #15; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 88: invokevirtual #16; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 91: invokevirtual #17; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 94: goto 11 97: return } 59. 知っていると役に立つ ( かもしれない )javap のオプション -c: 逆アセンブルする。一番よく使う -private: private なものを含む全てのメンバを表示 -c などと組み合わせて使う事が多い -verbose: クラスファイルのバージョン、コンスタントプールなどの詳細な情報を表示 -s: 内部的な型シグネチャの情報を表示 public static void main(java.lang.String[]) -> ([Ljava/lang/String;)V 60. 何が嬉しいの? Java バイトコードにコンパイルする俺言語 / フレームワーク等を作れるようになる バイトコード変換を利用したツールやフレームワークを作れるようになる AOP とか バイトコードにコンパイルする言語処理系やツールのバグなどを発見できるようになる 発表者は、 scala が生成したコードをよく javap している 面白い 61. まとめ Java バイトコードの マシンモデル 命令セット 型システム について簡単に解説 クラスファイルベリファイアの概要を説明 簡単なサンプルコードを逆アセンブルしてみた 知っていると役に立つかもしれない javap オプション 参考文献: Java 仮想マシン仕様第2版,ティム・リンドホルム、フランク・イェリン,ピアソン・エデュケーション Web で無料公開 http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html されてるので、そちらでも可 ( ただし英語 )