Movatterモバイル変換


[0]ホーム

URL:


TM
Uploaded byTaku Miyakawa
11,337 views

ラムダと invokedynamic の蜜月

Embed presentation

Downloaded 45 times
ラムダと invokedynamic の蜜月宮川 拓 / @miyakawa_taku2013-07-22 JJUG ナイトセミナー
自己紹介• 宮川 拓 (@miyakawa_taku) と申します• SI 屋です• JJUG 幹事です• Kink という JVM 言語を開発しています– https://bitbucket.org/kink/kink1
要旨• ラムダ式の実行は invokedynamic で実現されます。その理由と、実行の流れを見ます– 論点整理– ラムダ式の実行 (1)– invokedynamic の復習– ラムダ式の実行 (2)– なぜ invokedynamic?– ラムダの直列化2※注記この資料は、 JDK, JRE の「仕様」と「実装」を厳密に区別していません。
論点整理3
静的構造4関数型インタフェースラムダのクラスラムダのインスタンスinstance-ofimplementsComparableComparable<String> c = (x, y) -> x.length() - y.length();cc のクラス
実行の流れ5Comparable<String> c = (x, y) -> x.length() - y.length();Collections.sort(strings, c);main Collectionsラムダsort compare / 処理の中身の実行new / ラムダ式の実行今回の主な論点
ラムダ式の実行 (1)6
ラムダのクラスの実行時生成• 匿名クラスがコンパイル時に生成されるのに対し、ラムダのクラスは実行時に生成されます。まずはそれを確かめます7関数型インタフェースラムダのクラスラムダのインスタンスinstance-ofimplements実行時に生成
匿名クラスをコンパイル• 匿名クラスは、「外側のクラス名$連番」という名前で、コンパイラによって生成されます8$ cat >AnonClass.javaimport java.util.*;class AnonClass {Comparator<String> comparator() {return new Comparator<String> {@Override public int compare(String x, String y) {return x.length() – y.length();}};}}$ javac AnonClass.java && ls *.classAnonClass$1.classAnonClass.class
ラムダをコンパイル• 同等のラムダをコンパイルしても、対応するクラスファイルは生成されません9$ cat >Lambda.javaimport java.util.*;class Lambda {Comparator<String> comparator() {return (x, y) -> x.length() - y.length();}}$ javac Lambda.java && ls *.classLambda.class
ラムダのクラスの生成タイミング• ラムダのクラスが、コンパイル時には生成されないことが分かりました• したがって、実行時のどこかのタイミングで生成されているはずです10ラムダを含むソースクラスファイルラムダのクラスの生成ラムダのインスタンス化コンパイル (JDK) 実行 (JVM)この時点ではラムダのクラスは生成されないどこかのタイミング
ラムダのクラスの名前• まずはラムダのクラスの名前を確認します11$ cat >Lambda.javaimport java.util.*;public class Lambda {public static void main(String[] args) {Comparator<String> c = (x, y) -> x.length() - y.length();System.out.println(c.getClass());}}$ javac Lambda.java && java Lambdaclass Lambda$$Lambda$1
生成のタイミング• loadClass(name) すると、ラムダ式を実行するタイミングでラムダのクラスが出現していることが分かります12$ cat >Lambda.java...System.out.println(loadClassOrNull("Lambda$$Lambda$1"));Comparator<String> c = (x, y) -> x.length() - y.length();System.out.println(loadClassOrNull("Lambda$$Lambda$1"));...$ javac Lambda.java && java Lambdanullclass Lambda$$Lambda$1
ラムダ式実行の過程• ラムダ式を実行するタイミングで、ラムダのクラスが生成されていることが分かりました• ではラムダ式は、バイトコードのレベルでは、どのような過程で実行されているのでしょうか?13
ラムダ式のバイトコード• ラムダ式を含むプログラムのクラスファイルを、javap コマンドで逆アセンブルします14$ cat >Lambda.javaimport java.util.function.*;class Lambda {IntUnaryOperator adder(int delta) {return n -> n + delta;}}$ javap -c -p Lambda.class... (次ページ) ...
javap による逆アセンブルの結果15class Lambda {...java.util.function.IntUnaryOperator adder(int);Code:0: iload_11: invokedynamic #2, 06: areturnprivate static int lambda$0(int, int);Code:0: iload_11: iload_02: iadd3: ireturn}
再度 Java プログラム風に解釈• ラムダの処理の中身は lambda$0 というメソッドに記述されます• ラムダ式の実行は invokedynamic 命令の呼び出しになっています16class Lambda {IntUnaryOperator adder(int delta) {return <invokedynamic>(delta);}private static int lambda$0(int delta, int n) {return n + delta;}}
ここまでの整理• 分かったこと– ラムダのクラスはラムダ式実行の際に生成されます– ラムダ式の実行は invokedynamic 命令です• 推定できること– invokedynamic 命令をきっかけとして、ラムダのクラスが生成され、またラムダのインスタンスが生成されるはずです17
invokedynamic の復習18
invokedynamic の復習• ラムダ式実行の流れを追いかけるにあたり、まずは invokedynamic をおさらいします19
invokedynamic とは• 本来は、 JRuby など、 Java 以外の言語処理系のために、 Java SE 7 で追加されたメソッド呼び出し命令です• invokevirtual, invokeinterface など、 Java SE6 までのメソッド呼び出し命令と異なり、呼び出す処理が実行時に選択できます20
Java のメソッド呼び出し手順• どの呼び出し命令でも、手順は大体同じです21int result = receiver.doSomething(arg0, arg1);receiverarg0receiverarg1arg0receiver 戻り値invokexxxレシーバと引数をスタックに積む 呼び出し結果もスタックにvoid以外の場合
Java SE 6 までの呼び出し命令• Java SE 6 までの呼び出し命令は、いずれもJava 言語と密に結びついてます– メソッドは再定義されない– 名前、引数の型、レシーバのクラスが決まれば、呼び出すべき処理が定まる22invokestatic static メソッドを呼び出すinvokespecial コンストラクタ、 private メソッド等を呼び出すinvokevirtual クラスに属するメソッドを呼び出すinvokeinterface インタフェースに属するメソッドを呼び出す
Java 以外の言語処理系の実装―Java SE 6 以前• Java 言語にない機構(メソッド再定義など)を実現するため、処理系が呼び出しに介入→JVM による実行時最適化が効きづらい23array.joininvokevirtual処理系size Func@42join Func@123検索def join...invokevirtual関数テーブル
Java 以外の言語処理系の実装―Java SE 7 以降• invokedynamic を使って、処理系を介さずに、直接メソッドが呼び出せるようになりました→JVM による実行時最適化が効きやすい!24array.joininvokedynamic def join...
invokedynamic の道具立て• 呼び出し元 (CallSite) ごとに、ブートストラップメソッドで、呼び出し先の関数ポインタ(MethodHandle) を登録25MethodHandleオブジェクトCallSiteオブジェクトブートストラップメソッド <<create>>初回呼び出しの前に実行対象の処理呼び出し元<<create>>呼び出し
ブートストラップメソッド• static である必要がある• ブートストラップメソッドの引数– Lookup: MethodHandle のファクトリ– String: 「メソッド名」だが、使わなくても可– MethodType: invokedynamic の引数型と戻り値型– 任意個数の定数• ブートストラップメソッドの戻り値– MethodHandle の初期値が紐付けられた CallSite26
ブートストラップメソッドの例• メソッドを呼び出した後、強制的に戻り値を42 にする MethodHandle を生成27static CallSite bsm(Lookup lu, String name, MethodType mt)throws Exception {MethodHandle vmh = lu.findVirtual(mt.parameterType(0), name, vmt);return new ConstantCallSite(filterReturnValue(vmh,dropArguments(constant(int.class, 42), 0, int.class)));}• 以上のように、個々の invokedynamic の動作は、ブートストラップメソッドを見れば見当が付きます
ラムダ式の実行 (2)28
ラムダ式の invokedynamic• 先ほど見たところでは、ラムダ式の実行は、invokedynamic 命令の実行として実装されていました→紐付けられているブートストラップメソッドを見れば、実際の動作がわかるはずです29
ラムダ式の実行の流れ• ラムダ式の実行の invokedynamic には、java.lang.invoke.LambdaMetaFactory のmetaFactory メソッドがブートストラップメソッドとして紐付いています30invokedynamic<<ブートストラップ>>LambdaMetaFactory#metaFactoryラムダのインスタンス化2 回目以降の実行1. ラムダのクラスを生成2. ラムダをインスタンス化するMethodHandle を生成
LambdaMetaFactory#metaFactory31CallSite metafactory(Lookup lookup, // MethodHandle のファクトリString name, // 関数型インタフェースの唯一の抽象メソッド (SAM) の名前 (例: compare)MethodType invokedType, // 命令の引数・戻り値型MethodType samMethod, // SAM の引数・戻り値型MethodHandle implMethod, // 処理本体のメソッド (例: lambda$0)MethodType instantiatedSamType) // 型パラメータ適用後の SAM の引数・戻り値型シグネチャ1. これらの情報を元にラムダのクラスを生成• 引数をフィールドに格納するコンストラクタ• 処理本体のメソッドを呼び出す SAM の実装2. ラムダをインスタンス化する MethodHandle を生成、CallSite に紐付け
最終的に実行される処理32class Lambda {static class Lambda$1 implements IntUnaryOperator {private final int delta;Lambda$1(int delta) { this.delta = delta; }@Override public int applyAsInt(int n) {return lambda$0(this.delta, n);}}IntUnaryOperator adder(int delta) {return <invokedynamic: new Lambda$1(delta)>;}private static int lambda$0(int delta, int n) {return n + delta;}}metaFactoryが生成→ 結局、やってることは匿名クラスと(ほぼ)同じ!
なぜ invokedynamic?33
なぜ invokedynamic?• invokedynamic によるラムダ式の実行は、動作としては匿名クラスと似たようなものでした• なぜ、わざわざ invokedynamic を使うのでしょうか?→(1) クラスファイルが少なくなるおかげで、起動が速くなる、かもしれません→(2) JVM が LambdaMetaFactory を独自に実装することで、最適なインスタンス生成の方法を選択できるようになります34
(1) 起動時間• Java SE 8 では、 Streams API の採用によって、プログラム中で全面的にラムダ式が利用されることが想定されています(実態はどうあれ!)• その際、ラムダ式を匿名クラス方式で実装すると、クラスファイルの数が飛躍的に増えるため、クラスローディングが遅くなってしまいます• invokedynamic で、クラスを実行時に生成すれば、起動時間が抑えられる、かもしれません35
匿名クラスとラムダ式の起動時間比較• 5,000 個の匿名クラス/ラムダをインスタンス化するプログラムを実行(各10回)36平均2,041ms2,375msCPU: Core i3-2120T (2.6 GHz)OS: Arch Linux, カーネル: 3.9.9-1-ARCHJVM: JDK-8 build b99 (64-bit)
匿名クラス<ラムダ式 の考察• 匿名クラスの実行時間増加要因A) I/OB) jar の解凍• ラムダ式の実行時間増加要因C) ブートストラップメソッド呼び出し(それにともなう MethodHandle 作成など)D) バイトコード生成37A+B < C+D となった?
(2) インスタンス生成戦略の選択• LambdaMetaFactory は JVM が提供するAPI です。したがって、実行時に JVM に都合のよい方法でインスタンスが生成できます• 可能な選択肢:– 1 つのラムダ式ごとに 1 つのクラスを生成(既述)– 外部の値を参照しないラムダ式であれば、シングルトンインスタンスを戻す(後述)38
シングルトンインスタンス• 次のラムダ式は、外側の変数に依存していないため、何度実行しても、同じ働きのインスタンスを戻します→この場合、シングルトンインスタンスを毎回使い回せばいいはずです39Comparator<String> comparator() {return (x, y) -> x.length() - y.length();}
シングルトンインスタンス: 実行の流れ• ラムダの処理の本体が、外側の変数に依存していない場合、ラムダ式はシングルトンインスタンスを戻します40invokedynamic<<ブートストラップ>>LambdaMetaFactory#metaFactoryシングルトンインスタンス2 回目以降の実行1. ラムダのクラスを生成2. ラムダのシングルトンインスタンスを生成3. シングルトンインスタンスを戻すMethodHandle を生成
シングルトンインスタンス: 確認41$ cat >Lambda.javaimport java.util.*;public class Lambda {static Comparator<String> comparator() {return (x, y) -> x.length() - y.length();}public static void main(String[] args) {System.out.println(comparator());System.out.println(comparator());System.out.println(comparator());}}$ javac Lambda.java && java LambdaLambda$$Lambda$1@84aee7Lambda$$Lambda$1@84aee7Lambda$$Lambda$1@84aee7
その他の可能なインスタンス生成戦略• 1 つの関数型インタフェースごとに 1 つのクラスを生成。リフレクション経由で処理本体を呼び出し• MethodHandle を関数型インタフェースに直接ラップする機構を用意して、それを使う42
ラムダの直列化43
ラムダの直列化• 関数型インタフェースが Serializable を拡張している場合、ラムダのインスタンスは直列化できる必要があります– 直列化(ラムダ→バイト列)、非直列化(バイト列→ラムダ)した時、元のラムダと同じように機能する必要がある• ラムダのクラスが実行時に生成される時、どうしたら直列化・非直列化できるのでしょうか?→ writeReplace / readResolve を使う44
直列化の流れ45ラムダインスタンスSerializedLambda バイト列writeReplaceインタフェース名、処理本体のメソッド名など、ラムダを構成する静的情報を保持defaultWriteObject
非直列化の流れ46defaultReadObjectラムダインスタンスSerializedLambdaバイト列★★: 静的情報を元にラムダを復元SerializedLambda ラムダ式を含むクラスラムダreadResolve$deserializeLambda$<<create>>invokedynamicコンパイル時に生成
総括47
総括• ラムダ式は匿名クラスの単純な構文糖ではありません。 invokedynamic 命令を使って、クラスを実行時に生成しています• これにより、 JVM がラムダのインスタンスの生成方法を選べるので、実行時最適化の余地が大きくなります48
参考文献49
参考文献• Java SE 8 API Specification– http://download.java.net/jdk8/docs/api/overview-summary.html• JSR-335– http://jcp.org/en/jsr/detail?id=335• Brian Goetz “From Lambdas to Bytecode”– http://wiki.jvmlangsummit.com/images/1/1e/2011_Goetz_Lambda.pdf• 宮川 拓「Lambda 式に invokedynamic を使うのかもしれない話」– http://d.hatena.ne.jp/miyakawa_taku/20120728/134347848550

Recommended

PDF
Javaのログ出力: 道具と考え方
PPTX
今こそ知りたいSpring Web(Spring Fest 2020講演資料)
PDF
Swagger ではない OpenAPI Specification 3.0 による API サーバー開発
PDF
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
PDF
社内Java8勉強会 ラムダ式とストリームAPI
PDF
怖くないSpring Bootのオートコンフィグレーション
PPTX
GraalVMを3つの主機能から眺めてみよう(Oracle Groundbreakers APAC Virtual Tour 2020 講演資料)
PDF
クラスローダーについて
PPTX
さくっと理解するSpring bootの仕組み
PDF
ドメイン駆動設計のための Spring の上手な使い方
PDF
オブジェクト指向できていますか?
PDF
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
PDF
ストリーム処理を支えるキューイングシステムの選び方
PDF
JVMのGCアルゴリズムとチューニング
PDF
Mavenの真実とウソ
PDF
例外設計における大罪
PDF
Java によるクラウドネイティブ の実現に向けて
KEY
やはりお前らのMVCは間違っている
PDF
GraalVM の概要と、Native Image 化によるSpring Boot 爆速化の夢
PDF
バイトコードって言葉をよく目にするけど一体何なんだろう?(JJUG CCC 2022 Spring 発表資料)
PDF
あなたの知らないPostgreSQL監視の世界
PDF
20230105_TITECH_lecture_ishizaki_public.pdf
PDF
Spring native について
PDF
Dockerfileを改善するためのBest Practice 2019年版
PDF
文字コードに起因する脆弱性とその対策(増補版)
PDF
C#次世代非同期処理概観 - Task vs Reactive Extensions
PPTX
Guide to GraalVM (JJUG CCC 2019 Fall)
PDF
サービス開発における フロントエンド・ドメイン駆動設計の実践
PDF
JSR 352 “Batch Applications for the Java Platform”
PDF
Java 7 invokedynamic の概要

More Related Content

PDF
Javaのログ出力: 道具と考え方
PPTX
今こそ知りたいSpring Web(Spring Fest 2020講演資料)
PDF
Swagger ではない OpenAPI Specification 3.0 による API サーバー開発
PDF
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
PDF
社内Java8勉強会 ラムダ式とストリームAPI
PDF
怖くないSpring Bootのオートコンフィグレーション
PPTX
GraalVMを3つの主機能から眺めてみよう(Oracle Groundbreakers APAC Virtual Tour 2020 講演資料)
PDF
クラスローダーについて
Javaのログ出力: 道具と考え方
今こそ知りたいSpring Web(Spring Fest 2020講演資料)
Swagger ではない OpenAPI Specification 3.0 による API サーバー開発
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
社内Java8勉強会 ラムダ式とストリームAPI
怖くないSpring Bootのオートコンフィグレーション
GraalVMを3つの主機能から眺めてみよう(Oracle Groundbreakers APAC Virtual Tour 2020 講演資料)
クラスローダーについて

What's hot

PPTX
さくっと理解するSpring bootの仕組み
PDF
ドメイン駆動設計のための Spring の上手な使い方
PDF
オブジェクト指向できていますか?
PDF
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
PDF
ストリーム処理を支えるキューイングシステムの選び方
PDF
JVMのGCアルゴリズムとチューニング
PDF
Mavenの真実とウソ
PDF
例外設計における大罪
PDF
Java によるクラウドネイティブ の実現に向けて
KEY
やはりお前らのMVCは間違っている
PDF
GraalVM の概要と、Native Image 化によるSpring Boot 爆速化の夢
PDF
バイトコードって言葉をよく目にするけど一体何なんだろう?(JJUG CCC 2022 Spring 発表資料)
PDF
あなたの知らないPostgreSQL監視の世界
PDF
20230105_TITECH_lecture_ishizaki_public.pdf
PDF
Spring native について
PDF
Dockerfileを改善するためのBest Practice 2019年版
PDF
文字コードに起因する脆弱性とその対策(増補版)
PDF
C#次世代非同期処理概観 - Task vs Reactive Extensions
PPTX
Guide to GraalVM (JJUG CCC 2019 Fall)
PDF
サービス開発における フロントエンド・ドメイン駆動設計の実践
さくっと理解するSpring bootの仕組み
ドメイン駆動設計のための Spring の上手な使い方
オブジェクト指向できていますか?
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
ストリーム処理を支えるキューイングシステムの選び方
JVMのGCアルゴリズムとチューニング
Mavenの真実とウソ
例外設計における大罪
Java によるクラウドネイティブ の実現に向けて
やはりお前らのMVCは間違っている
GraalVM の概要と、Native Image 化によるSpring Boot 爆速化の夢
バイトコードって言葉をよく目にするけど一体何なんだろう?(JJUG CCC 2022 Spring 発表資料)
あなたの知らないPostgreSQL監視の世界
20230105_TITECH_lecture_ishizaki_public.pdf
Spring native について
Dockerfileを改善するためのBest Practice 2019年版
文字コードに起因する脆弱性とその対策(増補版)
C#次世代非同期処理概観 - Task vs Reactive Extensions
Guide to GraalVM (JJUG CCC 2019 Fall)
サービス開発における フロントエンド・ドメイン駆動設計の実践

Viewers also liked

PDF
JSR 352 “Batch Applications for the Java Platform”
PDF
Java 7 invokedynamic の概要
PDF
Project Lambdaの基礎
PDF
Lambda: A Peek Under The Hood - Brian Goetz
PDF
Java Batch 仕様 (Public Review時点)
PDF
JSFとJAX-RSで作る Thin Server Architecture #glassfishjp
JSR 352 “Batch Applications for the Java Platform”
Java 7 invokedynamic の概要
Project Lambdaの基礎
Lambda: A Peek Under The Hood - Brian Goetz
Java Batch 仕様 (Public Review時点)
JSFとJAX-RSで作る Thin Server Architecture #glassfishjp

Similar to ラムダと invokedynamic の蜜月

PDF
Lambda: A Peek Under The Hood [Java Day Tokyo 2015 6-3]
PPTX
Java8勉強会
PDF
Java8 lambdas chapter1_2
 
PPTX
Java8から始める関数型プログラミング
PDF
静かに変わってきたクラスファイルを詳細に調べて楽しむ(JJUG CCC 2024 Fall講演資料)
PPTX
【java8 勉強会】 怖くない!ラムダ式, Stream API
PPTX
jvmlang.daitokai 1.0.0 MinCamlJを作ってみた
PDF
InvokeDynamic at #shikadriven 2012
PPTX
C# 式木 (Expression Tree) ~ LINQをより深く理解するために ~
PPTX
Visual Studio による開発環境・プログラミングの進化
PPTX
Project lambda
Lambda: A Peek Under The Hood [Java Day Tokyo 2015 6-3]
Java8勉強会
Java8 lambdas chapter1_2
 
Java8から始める関数型プログラミング
静かに変わってきたクラスファイルを詳細に調べて楽しむ(JJUG CCC 2024 Fall講演資料)
【java8 勉強会】 怖くない!ラムダ式, Stream API
jvmlang.daitokai 1.0.0 MinCamlJを作ってみた
InvokeDynamic at #shikadriven 2012
C# 式木 (Expression Tree) ~ LINQをより深く理解するために ~
Visual Studio による開発環境・プログラミングの進化
Project lambda

More from Taku Miyakawa

PDF
Java SE 9の紹介: モジュール・システムを中心に
PDF
Graph Algorithms Part 1
PDF
Matrix Multiplication in Strassen Algorithm
PDF
擬似乱数生成器の評価
PDF
コルーチンの実装について
PDF
言語設計者が意味論を書くときに考えていたこと
PDF
金勘定のためのBigDecimalそしてMoney and Currency API
PDF
Quasar: Actor Model and Light Weight Threads on Java
PDF
Kink の宣伝
PDF
Java Quine Golf
PDF
Summary of "Hacking", 0x351-0x354
PDF
Processing LTSV by Apache Pig
PDF
Kink: プロトタイプベースの俺々 JVM 言語
PDF
Kink: invokedynamic on a prototype-based language
PDF
Java オブジェクトの内部構造
PDF
Hadoop jobbuilder
PDF
Kink: developing a programming language on the JVM
Java SE 9の紹介: モジュール・システムを中心に
Graph Algorithms Part 1
Matrix Multiplication in Strassen Algorithm
擬似乱数生成器の評価
コルーチンの実装について
言語設計者が意味論を書くときに考えていたこと
金勘定のためのBigDecimalそしてMoney and Currency API
Quasar: Actor Model and Light Weight Threads on Java
Kink の宣伝
Java Quine Golf
Summary of "Hacking", 0x351-0x354
Processing LTSV by Apache Pig
Kink: プロトタイプベースの俺々 JVM 言語
Kink: invokedynamic on a prototype-based language
Java オブジェクトの内部構造
Hadoop jobbuilder
Kink: developing a programming language on the JVM

ラムダと invokedynamic の蜜月


[8]ページ先頭

©2009-2025 Movatter.jp