目录
6、LambdaMetafactory.metafactory
在上一篇《Hotspot MethodHandle详解》中详细讲解了java.lang.invoke.MethodHandle的背景和使用,这个是invokedynamic指令实现的基础,本篇以Lamada的实现原理为例说明invokedynamic的具体用法以及C++字节码解释器中invokedynamic指令的源码实现。
一、Lamada的实现原理
1、字节码分析
想看一个典型的Lamada表达式,具体用法可以参考《java8 Lambda表达式》,代码如下:
package jni;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class LamadaTest {
public static void main(String[] args) {
List<String> test= Arrays.asList("a","b","c");
//完整的写法
// test.stream().forEach((String s)->{
// System.out.println("LamadaTest s->"+s);
// });
//简写版,lamada自己做类型推断
Consumer<String> consumer=s->System.out.println("LamadaTest s->"+s);
test.stream().forEach(consumer);
}
}
上述代码编译完成后执行javap -v查看具体的字节码指令,去除代码行号表等非关键部分后如下:
Constant pool:
#1 = Methodref #18.#43 // java/lang/Object."<init>":()V
#2 = Class #44 // java/lang/String
#3 = String #45 // a
#4 = String #46 // b
#5 = String #47 // c
#6 = Methodref #48.#49 // java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
#7 = InvokeDynamic #0:#55 // #0:accept:()Ljava/util/function/Consumer;
#8 = InterfaceMethodref #56.#57 // java/util/List.stream:()Ljava/util/stream/Stream;
#9 = InterfaceMethodref #58.#59 // java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V
#10 = Fieldref #60.#61 // java/lang/System.out:Ljava/io/PrintStream;
#11 = Class #62 // java/lang/StringBuilder
#12 = Methodref #11.#43 // java/lang/StringBuilder."<init>":()V
#13 = String #63 // LamadaTest s->
#14 = Methodref #11.#64 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#15 = Methodref #11.#65 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#16 = Methodref #66.#67 // java/io/PrintStream.println:(Ljava/lang/String;)V
#17 = Class #68 // jni/LamadaTest
#18 = Class #69 // java/lang/Object
#19 = Utf8 <init>
#20 = Utf8 ()V
#21 = Utf8 Code
#22 = Utf8 LineNumberTable
#23 = Utf8 LocalVariableTable
#24 = Utf8 this
#25 = Utf8 Ljni/LamadaTest;
#26 = Utf8 main
#27 = Utf8 ([Ljava/lang/String;)V
#28 = Utf8 args
#29 = Utf8 [Ljava/lang/String;
#30 = Utf8 test
#31 = Utf8 Ljava/util/List;
#32 = Utf8 consumer
#33 = Utf8 Ljava/util/function/Consumer;
#34 = Utf8 LocalVariableTypeTable
#35 = Utf8 Ljava/util/List<Ljava/lang/String;>;
#36 = Utf8 Ljava/util/function/Consumer<Ljava/lang/String;>;
#37 = Utf8 lambda$main$0
#38 = Utf8 (Ljava/lang/String;)V
#39 = Utf8 s
#40 = Utf8 Ljava/lang/String;
#41 = Utf8 SourceFile
#42 = Utf8 LamadaTest.java
#43 = NameAndType #19:#20 // "<init>":()V
#44 = Utf8 java/lang/String
#45 = Utf8 a
#46 = Utf8 b
#47 = Utf8 c
#48 = Class #70 // java/util/Arrays
#49 = NameAndType #71:#72 // asList:([Ljava/lang/Object;)Ljava/util/List;
#50 = Utf8 BootstrapMethods
#51 = MethodHandle #6:#73 // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#52 = MethodType #74 // (Ljava/lang/Object;)V
#53 = MethodHandle #6:#75 // invokestatic jni/LamadaTest.lambda$main$0:(Ljava/lang/String;)V
#54 = MethodType #38 // (Ljava/lang/String;)V
#55 = NameAndType #76:#77 // accept:()Ljava/util/function/Consumer;
#56 = Class #78 // java/util/List
#57 = NameAndType #79:#80 // stream:()Ljava/util/stream/Stream;
#58 = Class #81 // java/util/stream/Stream
#59 = NameAndType #82:#83 // forEach:(Ljava/util/function/Consumer;)V
#60 = Class #84 // java/lang/System
#61 = NameAndType #85:#86 // out:Ljava/io/PrintStream;
#62 = Utf8 java/lang/StringBuilder
#63 = Utf8 LamadaTest s->
#64 = NameAndType #87:#88 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#65 = NameAndType #89:#90 // toString:()Ljava/lang/String;
#66 = Class #91 // java/io/PrintStream
#67 = NameAndType #92:#38 // println:(Ljava/lang/String;)V
#68 = Utf8 jni/LamadaTest
#69 = Utf8 java/lang/Object
#70 = Utf8 java/util/Arrays
#71 = Utf8 asList
#72 = Utf8 ([Ljava/lang/Object;)Ljava/util/List;
#73 = Methodref #93.#94 // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#74 = Utf8 (Ljava/lang/Object;)V
#75 = Methodref #17.#95 // jni/LamadaTest.lambda$main$0:(Ljava/lang/String;)V
#76 = Utf8 accept
#77 = Utf8 ()Ljava/util/function/Consumer;
#78 = Utf8 java/util/List
#79 = Utf8 stream
#80 = Utf8 ()Ljava/util/stream/Stream;
#81 = Utf8 java/util/stream/Stream
#82 = Utf8 forEach
#83 = Utf8 (Ljava/util/function/Consumer;)V
#84 = Utf8 java/lang/System
#85 = Utf8 out
#86 = Utf8 Ljava/io/PrintStream;
#87 = Utf8 append
#88 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#89 = Utf8 toString
#90 = Utf8 ()Ljava/lang/String;
#91 = Utf8 java/io/PrintStream
#92 = Utf8 println
#93 = Class #96 // java/lang/invoke/LambdaMetafactory
#94 = NameAndType #97:#101 // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang