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