A Peek Under The Hood

Lambda:
A Peek Under The Hood
日本オラクル株式会社
Java SE
サステイニング エンジニアリング
バック デイビッド
Java Day Tokyo 2015
2015年4月8日
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
• 以下の事項は、弊社の一般的な製品の方向性に関する概要を説明するものです。
また、情報提供を唯一の目的とするものであり、いかなる契約にも組み込むこ
とはできません。以下の事項は、マテリアルやコード、機能を提供することを
コミットメント(確約)するものではないため、購買決定を行う際の判断材料
になさらないで下さい。オラクル製品に関して記載されている機能の開発、リ
リースおよび時期については、弊社の裁量により決定されます。
OracleとJavaは、Oracle Corporation 及びその子会社、関連会社の米国及びその他の国における登録商標です。
文中の社名、商品名等は各社の商標または登録商標である場合があります。
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
Oracle Confidential –
4
自己紹介
バック
• Java SE
デイビッド
サステイニング エンジニアリング
• JVM のバグを直す人
• 趣味:プログラミング
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
予定
• 背景・目標
• JSR-292 の紹介
– Invoke Dynamic
– MethodHandle
• ラムダ式の実装
– javac の出力
– Java SE Runtime の実装
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
目標
• ラムダ式に該当するバイトコードを理解する
• ラムダ式のパフォーマンスの影響を把握する
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
寄り道
• ラムダ式の実装を理解するには
– ラムダ式(Java 言語レベル)
– Java バイトコード
– JSR 292 (動的なメソッド呼び出し)
• JSR-292分かる方は少し珍しいので、今日先にカバーします。
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
JSR-292 (動的なメソッド呼び出し)
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
Da Vinci Machine Project
• Java 以外の言語を使っても、JVM はいい仮想マシン
– パフォーマンスがいい
– ポータビリティー(移植性)
– セキュリティ(バイトコード)
– 既存のフレームワークやライブラリ
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
JVM 言語(例)
• JVM専用
– Scala
– Clojure
– Groovy
– Ceylon
– Fortress
– Gosu
– Kotlin
• JVMへポートされた
– JRuby
– Jython
– Smalltalk
– Ada
– Scheme
– REXX
– Prolog
– Pascal
– Common LISP
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
言語ランタイムとは
Ruby Code
Java Code
Java
Class Library
JRuby Runtime
Java
Class Library
JVM
JVM
OS
OS
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
12
Java 以外の言語をより使いやすくする
• 継続 (continuations)
• 動的型付け呼び出し
• 末尾再帰
• インタフェース注入
• その他
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
Java 以外の言語をより使いやすくする
• 継続 (continuations)
• ネック:動的型付け呼び出し
• 末尾再帰
• インタフェース注入
• その他
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
従来のメソッドの呼び出し命令
• invokevirtual
– インスタンスメソッド
• invokeinterface
– インタフェースのメソッド
• invokestatic
– クラスメソッド
• invokespecial
– その他(コンストラクタ、スーパークラス、private など)
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
Java 言語に適切
他の言語にとっては利用出来ない場合がある
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
ディスパッチのエミュレーション
• JIT 最適化が出来ない
– 特に、インラインが出来ない
• エミュレーション自体のオーバーヘッドが高い
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
JSR-292
• Java 以外の言語の呼び出しロジックも直接サポートする
• 問題:
言語によってディスパッチのロジックが異なります
• 解決:
ディスパッチ ロジックを固定しない
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
JSR-292
• java.lang.invoke API
ディスパッチ ロジックを定義するため
• invokedynamic バイトコード命令
invoke API で定義したロジックを利用するディスパッチ
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
invokedynamic
• Indy (インディ)と呼ばれることが多い
• 最初は Java 言語で利用される予定はなかった
• java 言語に該当するものはない
• 歴史的
– 初めて命令が追加された
– 初めて JVM を他の言語のために変更しました
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
java.lang.invoke API
• MethodHandle
• CallSite
• Bootstrap Method (BSM)
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
MethodHandle
Method Handle
int foo()
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
22
MethodHandle
• メソッドを指定する
• 「関数ポインタ」(秘密!)
• 多相シグネチャ
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
CallSite
private void doStuff();
descriptor: ()V
flags: ACC_PRIVATE
Code:
stack=2, locals=2, args_size=1
0: new
#7
3: dup
4: invokespecial #8
7: astore_1
8: aload_1
9: aload_0
10: invokedynamic #9, 0
CS
Method Handle
int foo()
15: invokevirtual #10
18: return
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
24
CallSite
private void doStuff();
descriptor: ()V
flags: ACC_PRIVATE
Code:
stack=2, locals=2, args_size=1
0: new
#7
3: dup
4: invokespecial #8
7: astore_1
8: aload_1
9: aload_0
10: invokedynamic #9, 0
int bar()
int foo()
CS
15: invokevirtual #10
18: return
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
25
CallSite
• Indy の呼び出しを具象化する
• MethodHandle を持つ
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
Bootstrapping ステップ1
private void doStuff();
descriptor: ()V
flags: ACC_PRIVATE
Code:
stack=2, locals=2, args_size=1
0: new
#7
3: dup
4: invokespecial #8
7: astore_1
8: aload_1
9: aload_0
10: invokedynamic #9, 0
15: invokevirtual #10
18: return
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
27
Bootstrapping ステップ2
private void doStuff();
descriptor: ()V
flags: ACC_PRIVATE
Code:
stack=2, locals=2, args_size=1
0: new
#7
3: dup
4: invokespecial #8
7: astore_1
8: aload_1
9: aload_0
10: invokedynamic #9, 0
15: invokevirtual #10
18: return
BootStrap Method
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
28
Bootstrapping ステップ3
private void doStuff();
descriptor: ()V
flags: ACC_PRIVATE
Code:
stack=2, locals=2, args_size=1
0: new
#7
3: dup
4: invokespecial #8
7: astore_1
8: aload_1
9: aload_0
int foo()
10: invokedynamic #9, 0
15: invokevirtual #10
18: return
BootStrap Method
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
29
Bootstrapping ステップ4
private void doStuff();
descriptor: ()V
flags: ACC_PRIVATE
Code:
stack=2, locals=2, args_size=1
0: new
#7
3: dup
4: invokespecial #8
7: astore_1
8: aload_1
9: aload_0
10: invokedynamic #9, 0
15: invokevirtual #10
18: return
int foo()
CS
BootStrap Method
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
30
Bootstrapping ステップ5
private void doStuff();
descriptor: ()V
flags: ACC_PRIVATE
Code:
stack=2, locals=2, args_size=1
0: new
#7
3: dup
4: invokespecial #8
7: astore_1
8: aload_1
9: aload_0
10: invokedynamic #9, 0
int foo()
CS
15: invokevirtual #10
18: return
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
31
Bootstrap Method
• indy 命令の一回目の実行で呼ばれる
• CallSite を返す
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
Indy のライフサイクル
1回目の実行
1. 特定の Indy 命令が始めて実行される
2. bootstrap メソッドが呼ばれ、1の indy 命令に該当するメソッドを
選ぶ
3. bootstrap メソッドがこの indy 命令に該当する CallSite を生成し、戻
す。
4. CallSite の MethodHandle が指定するメソッドへジャンプ
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
Indy のライフサイクル
2回目からの実行
CallSite の MethodHandle が指定するメソッドへジャンプ
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
Invalidation
• 言語の runtime (JRuby, Groovy)がいつでも、CallSiteのMethodHandle を
自由に変更することが出来る
• 例:
– 引数のオブジェクトのタイプが違う
– タイプグラフが変更された
– 既にロードされたクラスのメソッドが動的にリプレースされる
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
JSR-292 まとめ
• リンク処理は言語ランタイムに任せる
• 汎用的なので、動的型付け言語ではなくても、利用出来る
• CallSite を変更しない限り、2回目の呼び出しは速い
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
ラムダ式の実装
バイトコード レベル
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
ラムダ式の型
新しい種類の型(関数型)を追加すると。。。
• バイトコード側でどうやって型を表現するか?
• 新規の関数インスタンスをどうやって生成する?
• variance (変位)をどうすべきか?
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
Variance (変位)
• String instanceof Object == true
• String[] instanceof Object[] == true
• ArrayList<String> instanceof ArrayList<Object> == false
• 関数は?
• (String -> Boolean) instanceof (Object -> Boolean) == ??
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
39
関数型インタフェース
• 一つの Abstract メソッドしか持たないインタフェース
• 別名 Single Abstract Method (SAM) インタフェース
• 既存のライブラリをそのまま利用出来る
• Java の開発者にとって一番自然
•例
– Runnable
– Comparator
– Executor
– ActionListener
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
関数型インタフェースのインスタンスを生成するには?
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
41
内部クラス
Thread t = new Thread(
() -> System.out.println("Hello Tokyo!")
);
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
内部クラス
Thread t = new Thread(
new Runnable() {
public void run() {
System.out.println("Hello Tokyo!");
}
}
);
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
内部クラスの欠点
• ラムダ式ごとに一つのクラスが生成される
• ラムダ式ごとに new が呼ばれる
• タイプ汚染
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
DEMO (内部クラス)
public class Worker {
public void doWork(Runnable r) {
r.run();
}
}
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
45
他の選択肢
• メソッドハンドル
– 多相シグネチャの型消去の問題
• dynamic proxies か MethodHandleProxy
– パフォーマンスが問題
• JVM の内部API を解してクラスを作る
• Wrapper Class
(インタフェース毎に一つの実装クラス)
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
質問
ラムダ式をどうやって、バイトコードで表現し、実行する?
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
47
実は、2つの質問!
どうやってバイトコードで表現する?
どうやって実行する?
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
48
2つの話
• ラムダ式
==「何をやって欲しいか」
– バイトコードの表現を固定する必要がある
• 内部クラス、MethodHandle、など==「どうやって実行するか」
– ベストの答えがない
– 実装を固定したくはない
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
解決
• "All problems in computer science can be solved by another level of
indirection“
コンピュータ科学のいかなる問題も他のレベルの
インダイレクションによって解決できる
-David Wheeler 氏
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
解決
• バイトコードで実行方法を指定しないこと
– 実行は Runtime に任せることが出来る
– Runtime によって実装を自由に変えることが出来る
– javac より JVM のほうが判断力は適切
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
ラムダ式のレシピ
• メソッドの本体(body)のバイトコード (ある場合)
• 生成する関数型インタフェース (Runnable など)
• 構文(lexical)スコープから取得した値
• 他のメタデータ(serializable など)
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
ラムダ式のレシピ(メソッドの本体)
String msg = "Hello Tokyo!"
Thread t = new Thread(
() -> System.out.println(msg)
);
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
53
ラムダ式のレシピ(関数型インタフェース)
java.lang.Runnable
String msg = "Hello Tokyo!"
Thread t = new Thread(
() -> System.out.println(msg)
);
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
54
ラムダ式のレシピ (取得した値)
String msg = "Hello Tokyo!"
Thread t = new Thread(
() -> System.out.println(msg)
);
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
55
ラムダ式のレシピ他の(メタデータ)
(今回は特に無し)
String msg = "Hello Tokyo!"
Thread t = new Thread(
() -> System.out.println(msg)
);
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
56
レシピを JSR-292 で表現出来る
• Indy の呼び出し
– 関数型インタフェースのインスタンスを戻す
– Lambda Factory と呼ばれます
• MethodHandle / CallSite
– ラムダ式の「本体」メソッドを指す
• Bootstrap method (BSM)
– 上記 MethodHandle / CallSite を戻す
– Lambda Factory を用意する(つくる)
– ので、LambdaMetaFactory と呼ばれる
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
ラムダの工場
• Indy の呼び出しはラムダ式を作るので「lambda factory」(ラムダ工
場)と呼ばれます。
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
ラムダ工場を作る工場
• Lambda factory (invoke dynamic) に該当する
Callsite を用意する bootstrap method は
lambda meta-facotry と呼ばれます。
• java.lang.invoke.LambdaMetaFactory
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
ラムダ式の CallSite
• 動的型付けのタイプではない
• Bootstrap メソッドによって生成されたら、変わらない(固定
CallSite)
• 2回目の呼び出しから普通のコールと同じパフォーマンス
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
Demo (ラムダ式)
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
61
ラムダ式の実装
Oracle Java SE 8 の実装
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
62
現在の Java SE の実装
• 内部クラスを生成する
• ASM を利用する
• 構文スコープの値を取得しない場合、singleton を利用する
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
裏を覗いてみよう
• jdk/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
• 下記のシステムプロパティで生成されたクラスをダンプすることが
出来る。
jdk.internal.lambda.dumpProxyClasses
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
まとめ
• バイトコードレベルでラムダ式のレシピだけがある。
• 実現方法は Java のランタイムに依存します。
• パフォーマンス
現在の実装
>=
内部クラス
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
Thank You!
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
オマケ
David Wheeler 氏曰く
• All problems in computer science can be solved by another level of
indirection, except of course for the problem of too many indirections.
コンピュータ科学のいかなる問題も他のレベルのインダイレクショ
ンによって解決できる。ただし、あまりに間接参照のレイヤーが重
なり過ぎるという問題だけは解決できない。
• Compatibility means deliberately repeating other people's mistakes.
互換性は他の人々の間違いを意図的に繰り返すことを意味する
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
Safe Harbor Statement
The preceding is intended to outline our general product direction. It is intended for
information purposes only, and may not be incorporated into any contract. It is not a
commitment to deliver any material, code, or functionality, and should not be relied upon
in making purchasing decisions. The development, release, and timing of any features or
functionality described for Oracle’s products remains at the sole discretion of Oracle.
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
68
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. |
69