Advertisement

Java 7 invokedynamic の概要

Feb. 17, 2012
Advertisement

More Related Content

Advertisement

Recently uploaded(20)

Advertisement

Java 7 invokedynamic の概要

  1. Java 7 invokedynamic の概要 @miyakawa_taku 2012-02-22 JJUG Night Seminar
  2. 1 発表者 • 名前: 宮川 拓 • 職業: SI 屋で Hadooper • オレオレ JVM 言語 Kink を開発中 – Its’ fun!
  3. 2 invokedynamic とは? • Java 6 までの JVM = Java のための仮想マシン • Java 7 の JVM = Java + Java 以外の言語のための仮想マシン • invokedynamic は Java 以外の言語のために 追加された新しいメソッド呼び出し命令
  4. 3 論点 • Java 6 までの呼び出し命令 – JVM 命令は Java のために作られていた • Java 以外の言語 – 既存の命令セットでは Java 以外の言語処理系が 効率的に実装できない • invokedynamic – 新しい命令で Java 以外の言語も効率的になる
  5. 4 論点 • Java 6 までの呼び出し命令 • Java 以外の言語 • invokedynamic
  6. 5 Java 6 までの呼び出し命令 • 4 種類 • Java のメソッド呼び出しはこれで全部 OK invokestatic static メソッドを呼び出す invokespecial コンストラクタ、 private メソッド等を呼び出す invokevirtual クラスに属するメソッドを呼び出す (非 private) invokeinterface インタフェースに属するメソッドを呼び出す
  7. 6 invokestatic • static メソッドの呼び出し → invokestatic StringUtils.rjust("VOXXX", 10, '.'); javac ldc "VOXXX" // "VOXXX” をスタックに積む bipush 10 // 10 をスタックに積む bipush 46 // '.' = 46 をスタックに積む invokestatic StringUtils#rjust(String,int,char):String
  8. 7 invokestatic • invokestatic 命令は与えられたメソッドをその まま呼び出す class StringUtils bang! static String rjust(String,int,char) invokestatic iload_1 aload_0 ...
  9. 8 invokespecial • private なインスタンスメソッド、コンストラクタ、 super メソッドの呼び出し → invokespecial this.checkIndex(index); javac aload_0 // this (レシーバ) をスタックに積む iload_1 // index をスタックに積む invokespecial MyList#checkIndex(int):void
  10. 9 invokespecial • invokespecial 命令は与えられたメソッドをその まま呼び出す (invokestatic とほぼ同じ) class MyList bang! private void checkIndex(int) invokespecial iconst_0 iload_1 ...
  11. 10 invokevirtual • とある GUI ツールキットのクラス階層 Widget Rect getRect() Button void push() CheckBox OKButton void push() void push() boolean isPushed()
  12. 11 invokevirtual • レシーバの型がクラスであるインスタンスメ ソッド呼び出し (除 private) → invokevirtual Button button; button.push(); javac aload_0 invokevirtual Button#push():void
  13. 12 invokevirtual • invokevirtual 命令はレシーバのクラスが持つ メソッドのルックアップテーブル (vtable) を見 て呼び出すメソッドを決める Button CheckBox OKButton getRect getRect getRect invokevirtual push push push Button クラスの isPushed 2 番目のメソッド を呼ぶ! メソッドへのポインタが 格納されている
  14. 13 invokeinterface • 再び GUI ツールキットのクラス階層 intf Movable intf Resizable void move(Point) void resize(Size) class Icon class Frame void move(Point) void resize(Size) void move(Point)
  15. 14 invokeinterface • レシーバの型がインタフェースであるインスタ ンスメソッド呼び出し → invokeinterface Movable window; Point point; window.move(new Point(0, 0)); javac aload_0 aload_1 invokeinterface Movable#move(Point):void
  16. 15 invokeinterface • invokeinterface 命令はレシーバのクラスがイ ンタフェース毎に持つルックアップテーブル (itable) を見て呼び出すメソッドを決める class Icon class Frame Movable Movable Resizable invokeinterface move move resize Movable インタフェースの 1 番目のメソッドを呼ぶ! メソッドへのポインタが 格納されている
  17. 16 Java 6 までの呼び出し命令 (rep) • 4 つの呼び出し命令で Java のメソッド呼び出 しをカバー • Java に特化した仕組み – メソッドが再定義されることはない – 単一継承 – 名前と型の組み合わせによるメソッドの特定 – レシーバのクラスによる単一ディスパッチ
  18. 17 論点 • Java 6 までの呼び出し命令 • Java 以外の言語 • invokedynamic
  19. 18 古い革袋と新しい酒 Scheme JS Python Groovy Ruby Scala Java Java JVM JVM むかし 最近
  20. 19 古い革袋と新しい酒 • JVM は Java のために作られたので、それ以外 の言語を動かすのには工夫が必要 • 例: Ruby ではメソッドが再定義できるので、既 存の呼び出し命令で直接呼び出せない • 他にも – method-missing – 多重継承 – mix-in
  21. 20 Java 6 までは呼び出すために 処理系が間に挟まる必要があった array.join バイトコード 生成 invoke invoke CAFE 0000 virtual virtual def join 3939 5151 処理系 ... ...... ... 検索 size Func@42 join Func@123
  22. 21 本当はこうしたい! array.join バイトコード 生成 CAFE 0000 bang! def join 3939 5151 ... ...... ... 余計な処理がなく、JIT コンパイラに よる最適化が掛けやすくなる
  23. 22 新しい呼び出し命令が欲しい • Java 以外の言語のメソッドを JVM の命令で直 接呼び出したい • しかも – 実行される処理を独自のルールで検索したい – メソッドを実行時に繋ぎ変えたい – JIT でカリカリに最適化してほしい
  24. 23 論点 • Java 6 までの呼び出し命令 • Java 以外の言語 • invokedynamic
  25. 24 invokedynamic • invokedynamic による呼び出しが実現すること – 呼び出す処理の選択をプログラムによって制御で きる – 呼び出す処理を実行時に繋ぎ変えられる – カリカリに最適化して実行する
  26. 25 invokedynamic の基本コンセプト • やりたいこと – 命令ごとに関数ポインタを登録、これが指し示す 先の処理を呼び出す – 別の関数ポインタを登録しなおすことも可能 invokedynamic 関数 bang! 対象の ポインタ 処理 あとから 貼り替えられる
  27. 26 invokedynamic の道具立て • 仔細に道具立てを見ると下図の通り 初回実行時に invokedynamic 呼び出し bootstrap <<create>> CallSite メソッド 任意の MH の Method bang! 対象の 処理 再登録 Handle 処理
  28. 27 MethodHandle • まずは MethodHandle 初回実行時に invokedynamic 呼び出し bootstrap <<create>> CallSite メソッド 任意の MH の Method bang! 対象の 処理 再登録 Handle 処理
  29. 28 MethodHandle • MethodHandle は型付き関数ポインタ • 決まった型・数の引数をスタックから取り、結 果をスタックに置く処理を指し示す プリミティブな MethodHandle の作成 Lookup#findVirtual • インスタンスメソッドを呼び出す MH Lookup#findConstructor • インスタンスを生成する MH Lookup#findGetter • フィールドの値を返す MH MethodHandles#constant • 定数値を返す MH
  30. 29 MethodHandle • MethodHandle は合成したり、引数の順序を 入れ替えたり、部分適用したりして新しい MethodHandle を生成できる 複合的な MethodHandle の作成 MethodHandles • (if test then target else fallback) を行う #guardWithTest MH MethodHandles • 本処理の戻り値に後処理を加える MH #filterReturnValue MethodHandle • 先頭の引数の値を固定した MH #bindTo
  31. 30 CallSite • ついで CallSite 初回実行時に invokedynamic 呼び出し bootstrap <<create>> CallSite メソッド 任意の MH の Method bang! 対象の 処理 再登録 Handle 処理
  32. 31 CallSite • 1 つの invokedynamic 命令に紐付いて「呼び 出し元」を表す • MethodHandle の参照を保持する CallSite の具象クラス ConstantCallSite • MH が書き換えられない • private final MethodHandle mh MutableCallSite • MH が書き換えられる • private MethodHandle mh VolatileCallSite • MH が書き換えられる • private volatile MethodHandle mh
  33. 32 bootstrap メソッド • 最後に bootstrap メソッド 初回実行時に invokedynamic 呼び出し bootstrap <<create>> CallSite メソッド 任意の MH の Method bang! 対象の 処理 再登録 Handle 処理
  34. 33 bootstrap メソッド • 各 invokedynamic 命令は bootstrap という static メソッドへの参照を持っている • invokedynamic 命令が最初に実行される時に bootstrap メソッドが呼ばれて – 命令に CallSite オブジェクトを紐付ける – MethodHandle の初期値を CallSite に紐付ける
  35. 34 bootstrap メソッド • 例: 戻り値の型が int の場合、強制的に値を 42 にする static CallSite bsm(Lookup lu, String name, MethodType mt) throws Exception { MethodType vmt = mt.dropParameterTypes(0, 1); MethodHandle vmh = lu.findVirtual(mt.parameterType(0), name, vmt); if (vmt.returnType() == int.class) return new ConstantCallSite(filterReturnValue(vmh, dropArguments(constant(int.class, 42), 0, int.class))); else return new ConstantCallSite(vmh); }
  36. 35 bootstrap メソッド • 例: 「メソッド名」を文字列として戻す – indy での「メソッド名」は bootstrap に渡す引数に 過ぎない static CallSite bsm(Lookup lu, String name, MethodType mt) { return new ConstantCallSite(dropArguments( constant(String.class, name), 0, mt.parameterType(0))); } MethodType が引数なし 戻り値 String でないとエラー
  37. 36 invokedynamic (rep) • bootstrap メソッドにより、命令に紐付く処理を 言語処理系独自のやり方で選択できる • CallSite に新しい MethodHandle を登録するこ とにより、命令に紐付く処理を実行時に変更 できる • これらの仕組みを JVM のレベルでサポートす るため、賢く使うと効率的な言語処理系が実 装できる
  38. 37 蛇足 • MethodHandle は処理をオブジェクトとして 様々に操作できるから面白い! • たとえば MethodHandle ベースの AOP フレー ムワークが作れるかも
  39. 38 参考 • Da Vinci Machine Project (mlvm) – http://openjdk.java.net/projects/mlvm/ • JSR 292 Cookbook – http://cr.openjdk.java.net/~jrose/pres/200906-Cookbook.htm • John Rose’ weblog at Oracle: dynamic invocation in the VM – https://blogs.oracle.com/jrose/entry/dynamic_invocation_in_the_vm • Optimizing invokedynamic – http://dl.acm.org/citation.cfm?id=1852763 • HotSpot Internals for OpenJDK: CallingSequences – https://wikis.oracle.com/display/HotSpotInternals/CallingSequences
Advertisement