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