java 7 invokedynamic の概要

39
Java 7 invokedynamic の概要 @miyakawa_taku 2012-02-22 JJUG Night Seminar

Upload: taku-miyakawa

Post on 08-Jul-2015

7.280 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Java 7 invokedynamic の概要

Java 7 invokedynamic の概要

@miyakawa_taku

2012-02-22

JJUG Night Seminar

Page 2: Java 7 invokedynamic の概要

発表者

• 名前: 宮川 拓

• 職業: SI 屋で Hadooper

• オレオレ JVM 言語 Kink を開発中

– Its’ fun!

1

Page 3: Java 7 invokedynamic の概要

invokedynamic とは?

• Java 6 までの JVM = Java のための仮想マシン

• Java 7 の JVM = Java + Java 以外の言語のための仮想マシン

• invokedynamic は Java 以外の言語のために追加された新しいメソッド呼び出し命令

2

Page 4: Java 7 invokedynamic の概要

論点

• Java 6 までの呼び出し命令

– JVM 命令は Java のために作られていた

• Java 以外の言語

–既存の命令セットでは Java 以外の言語処理系が効率的に実装できない

• invokedynamic

–新しい命令で Java 以外の言語も効率的になる

3

Page 5: Java 7 invokedynamic の概要

論点

• Java 6 までの呼び出し命令

• Java 以外の言語

• invokedynamic

4

Page 6: Java 7 invokedynamic の概要

Java 6 までの呼び出し命令

• 4 種類

• Java のメソッド呼び出しはこれで全部 OK

5

invokestatic static メソッドを呼び出す

invokespecial コンストラクタ、 private メソッド等を呼び出す

invokevirtual クラスに属するメソッドを呼び出す (非 private)

invokeinterface インタフェースに属するメソッドを呼び出す

Page 7: Java 7 invokedynamic の概要

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

Page 8: Java 7 invokedynamic の概要

invokestatic

• invokestatic 命令は与えられたメソッドをそのまま呼び出す

7

invokestatic

class StringUtils static String rjust(String,int,char) iload_1 aload_0 ...

bang!

Page 9: Java 7 invokedynamic の概要

invokespecial

• private なインスタンスメソッド、コンストラクタ、super メソッドの呼び出し → invokespecial

8

this.checkIndex(index);

aload_0 // this (レシーバ) をスタックに積む iload_1 // index をスタックに積む invokespecial MyList#checkIndex(int):void

javac

Page 10: Java 7 invokedynamic の概要

invokespecial

• invokespecial 命令は与えられたメソッドをそのまま呼び出す (invokestatic とほぼ同じ)

9

class MyList private void checkIndex(int) iconst_0 iload_1 ...

invokespecial bang!

Page 11: Java 7 invokedynamic の概要

invokevirtual

• とある GUI ツールキットのクラス階層

10

Button

void push()

CheckBox

void push()

boolean isPushed()

Widget

Rect getRect()

OKButton

void push()

Page 12: Java 7 invokedynamic の概要

invokevirtual

• レシーバの型がクラスであるインスタンスメソッド呼び出し (除 private) → invokevirtual

11

Button button; button.push();

javac

aload_0 invokevirtual Button#push():void

Page 13: Java 7 invokedynamic の概要

invokevirtual

• invokevirtual 命令はレシーバのクラスが持つメソッドのルックアップテーブル (vtable) を見て呼び出すメソッドを決める

12

invokevirtual

Button

getRect

push

CheckBox

getRect

push

isPushed

OKButton

getRect

push

Button クラスの 2 番目のメソッド を呼ぶ!

メソッドへのポインタが 格納されている

Page 14: Java 7 invokedynamic の概要

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)

Page 15: Java 7 invokedynamic の概要

invokeinterface

• レシーバの型がインタフェースであるインスタンスメソッド呼び出し → invokeinterface

14

Movable window; Point point; window.move(new Point(0, 0));

javac

aload_0 aload_1 invokeinterface Movable#move(Point):void

Page 16: Java 7 invokedynamic の概要

invokeinterface

• invokeinterface 命令はレシーバのクラスがインタフェース毎に持つルックアップテーブル (itable) を見て呼び出すメソッドを決める

15

invokeinterface

Movable インタフェースの 1 番目のメソッドを呼ぶ!

class Icon class Frame

Movable

move

Movable

move

Resizable

resize

メソッドへのポインタが 格納されている

Page 17: Java 7 invokedynamic の概要

Java 6 までの呼び出し命令 (rep)

• 4 つの呼び出し命令で Java のメソッド呼び出しをカバー

• Java に特化した仕組み

– メソッドが再定義されることはない

–単一継承

–名前と型の組み合わせによるメソッドの特定

– レシーバのクラスによる単一ディスパッチ

16

Page 18: Java 7 invokedynamic の概要

論点

• Java 6 までの呼び出し命令

• Java 以外の言語

• invokedynamic

17

Page 19: Java 7 invokedynamic の概要

古い革袋と新しい酒

18

むかし 最近

JVM

Java

JVM

Java Ruby Groovy

Python Scheme

Scala

JS

Page 20: Java 7 invokedynamic の概要

古い革袋と新しい酒

• JVM は Java のために作られたので、それ以外の言語を動かすのには工夫が必要

• 例: Ruby ではメソッドが再定義できるので、既存の呼び出し命令で直接呼び出せない

• 他にも

– method-missing

–多重継承

– mix-in

19

Page 21: Java 7 invokedynamic の概要

Java 6 までは呼び出すために 処理系が間に挟まる必要があった

20

array.join

CAFE 0000 3939 5151 ......

バイトコード 生成

invoke virtual

処理系

size Func@42

join Func@123

検索

def join ... ...

invoke virtual

Page 22: Java 7 invokedynamic の概要

本当はこうしたい!

21

array.join

CAFE 0000 3939 5151 ......

バイトコード 生成

bang! def join ... ...

余計な処理がなく、JIT コンパイラによる最適化が掛けやすくなる

Page 23: Java 7 invokedynamic の概要

新しい呼び出し命令が欲しい

• Java 以外の言語のメソッドを JVM の命令で直接呼び出したい

• しかも

–実行される処理を独自のルールで検索したい

– メソッドを実行時に繋ぎ変えたい

– JIT でカリカリに最適化してほしい

22

Page 24: Java 7 invokedynamic の概要

論点

• Java 6 までの呼び出し命令

• Java 以外の言語

• invokedynamic

23

Page 25: Java 7 invokedynamic の概要

invokedynamic

• invokedynamic による呼び出しが実現すること

–呼び出す処理の選択をプログラムによって制御できる

–呼び出す処理を実行時に繋ぎ変えられる

–カリカリに最適化して実行する

24

Page 26: Java 7 invokedynamic の概要

invokedynamic の基本コンセプト

• やりたいこと

–命令ごとに関数ポインタを登録、これが指し示す先の処理を呼び出す

–別の関数ポインタを登録しなおすことも可能

25

invokedynamic

対象の 処理

関数 ポインタ

あとから 貼り替えられる

bang!

Page 27: Java 7 invokedynamic の概要

invokedynamic の道具立て

• 仔細に道具立てを見ると下図の通り

26

invokedynamic

Method Handle

CallSite bootstrap メソッド

<<create>>

初回実行時に 呼び出し

bang! 対象の 処理

任意の 処理

MH の 再登録

Page 28: Java 7 invokedynamic の概要

MethodHandle

• まずは MethodHandle

27

invokedynamic

Method Handle

CallSite bootstrap メソッド

<<create>>

初回実行時に 呼び出し

bang! 対象の 処理

任意の 処理

MH の 再登録

Page 29: Java 7 invokedynamic の概要

MethodHandle

• MethodHandle は型付き関数ポインタ

• 決まった型・数の引数をスタックから取り、結果をスタックに置く処理を指し示す

28

プリミティブな MethodHandle の作成

Lookup#findVirtual • インスタンスメソッドを呼び出す MH

Lookup#findConstructor • インスタンスを生成する MH

Lookup#findGetter • フィールドの値を返す MH

MethodHandles#constant • 定数値を返す MH

Page 30: Java 7 invokedynamic の概要

MethodHandle

• MethodHandle は合成したり、引数の順序を入れ替えたり、部分適用したりして新しい MethodHandle を生成できる

29

複合的な MethodHandle の作成

MethodHandles #guardWithTest

• (if test then target else fallback) を行う MH

MethodHandles #filterReturnValue

• 本処理の戻り値に後処理を加える MH

MethodHandle #bindTo

• 先頭の引数の値を固定した MH

Page 31: Java 7 invokedynamic の概要

CallSite

• ついで CallSite

30

invokedynamic

Method Handle

CallSite bootstrap メソッド

<<create>>

初回実行時に 呼び出し

bang! 対象の 処理

任意の 処理

MH の 再登録

Page 32: Java 7 invokedynamic の概要

CallSite

• 1 つの invokedynamic 命令に紐付いて「呼び出し元」を表す

• MethodHandle の参照を保持する

31

CallSite の具象クラス

ConstantCallSite • MH が書き換えられない • private final MethodHandle mh

MutableCallSite • MH が書き換えられる • private MethodHandle mh

VolatileCallSite • MH が書き換えられる • private volatile MethodHandle mh

Page 33: Java 7 invokedynamic の概要

bootstrap メソッド

• 最後に bootstrap メソッド

32

invokedynamic

Method Handle

CallSite bootstrap メソッド

<<create>>

初回実行時に 呼び出し

bang! 対象の 処理

任意の 処理

MH の 再登録

Page 34: Java 7 invokedynamic の概要

bootstrap メソッド

• 各 invokedynamic 命令は bootstrap という static メソッドへの参照を持っている

• invokedynamic 命令が最初に実行される時に bootstrap メソッドが呼ばれて

–命令に CallSite オブジェクトを紐付ける

– MethodHandle の初期値を CallSite に紐付ける

33

Page 35: Java 7 invokedynamic の概要

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); }

Page 36: Java 7 invokedynamic の概要

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 でないとエラー

Page 37: Java 7 invokedynamic の概要

invokedynamic (rep)

• bootstrap メソッドにより、命令に紐付く処理を言語処理系独自のやり方で選択できる

• CallSite に新しい MethodHandle を登録することにより、命令に紐付く処理を実行時に変更できる

• これらの仕組みを JVM のレベルでサポートするため、賢く使うと効率的な言語処理系が実装できる

36

Page 38: Java 7 invokedynamic の概要

蛇足

• MethodHandle は処理をオブジェクトとして様々に操作できるから面白い!

• たとえば MethodHandle ベースの AOP フレームワークが作れるかも

37

Page 39: Java 7 invokedynamic の概要

参考

• 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