Java 7 invokedynamic の概要

9,057 views

Published on

Published in: Technology

Java 7 invokedynamic の概要

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

×