java 8 lambda 底层实现原理
之前有听说过 java 8 的 lambda 实现不再是java 1.5的匿名内部类语法糖。今天刚好有时间看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class App {
@FunctionalInterface public interface LambdaDemo{ public void runLambda(); } public static void doSomething(LambdaDemo demo){ demo.runLambda(); }
public static void main(String[] args) { doSomething(()->System.out.println("hello world!")); } }
|
经过编译后,生成了两个 class 文件:
1 2 3
| $ ls App.class 'App$LambdaDemo.class'
|
显然,App$LambdaDemo.class
是interface lambdaDemo的 class 文件。我们可以用命令查看下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| $ javap -c -v App\$LambdaDemo 警告: 二进制文件App$LambdaDemo包含cn.victor.study.App$LambdaDemo Classfile /Users/chutian/IdeaProjects/demos/target/classes/cn/victor/study/App$LambdaDemo.class Last modified 2019-3-2; size 283 bytes MD5 checksum af7c9127a2a697374979d7c62f0fb056 Compiled from "App.java" public interface cn.victor.study.App$LambdaDemo minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT Constant pool: { public abstract void runLambda(); descriptor: ()V flags: ACC_PUBLIC, ACC_ABSTRACT } SourceFile: "App.java" RuntimeVisibleAnnotations: 0: InnerClasses: public static
|
接下来看看lambda 究竟是如何实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
| $ javap -c -v App 警告: 二进制文件App包含cn.victor.study.App Classfile /Users/chutian/IdeaProjects/demos/target/classes/cn/victor/study/App.class Last modified 2019-3-2; size 1353 bytes MD5 checksum 7c3e6a94f67374296a68af43fa788f7b Compiled from "App.java" public class cn.victor.study.App minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: { public cn.victor.study.App(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial 4: return LineNumberTable: line 7: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcn/victor/study/App;
public static void doSomething(cn.victor.study.App$LambdaDemo); descriptor: (Lcn/victor/study/App$LambdaDemo;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokeinterface 6: return LineNumberTable: line 14: 0 line 15: 6 LocalVariableTable: Start Length Slot Name Signature 0 7 0 demo Lcn/victor/study/App$LambdaDemo;
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=1, args_size=1 0: invokedynamic 5: invokestatic 8: return LineNumberTable: line 18: 0 line 19: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String;
private static void lambda$main$0(); descriptor: ()V flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC Code: stack=2, locals=0, args_size=0 0: getstatic 3: ldc 5: invokevirtual 8: return LineNumberTable: line 18: 0 } SourceFile: "App.java" InnerClasses: public static public static final BootstrapMethods: 0: Method arguments:
|
注意看 main 方法的字节码,方法的 code 属性里:
1 2 3
| 0: invokedynamic #3, 0 5: invokestatic #4 8: return
|
lambda 的实现是通过 invokedynamic 指令来实现的。java虚拟机里调用方法的字节码指令有5种:
- invokestatic //调用静态方法
- invokespecial //调用私有方法、实例构造器方法、父类方法
- invokevirtual //调用实例方法
- invokeinterface //调用接口方法,会在运行时再确定一个实现此接口的对象
- invokedynamic //先在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法,在此之前的4条调用指令,分派逻辑是固化在java虚拟机内部的,而invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的。
invokedynamic指令是在jvm7中新增的,invokedynamic出现的位置代表一个动态调用点 invokedynamic指令后面会跟一个指向常量池的调用点限定符,这个限定符会被解析为一个动态调用点。调用点限定符的符号引用为CONSTANT_InvokeDynamic_info结构。
1 2 3 4 5
| CONSTANT_InvokeDynamic_info{ u1 tag; u2 bootstrap_method_attr_index; u2 name_and_type_index; }
|
我们再看invokedynamic #3
中#3指向的线程池常量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #3 = InvokeDynamic #0:#37
#37 = NameAndType #47:#50
BootstrapMethods: 0: #34 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; Method arguments: #35 ()V #36 invokestatic cn/victor/study/App.lambda$main$0:()V #35 ()V
#36 = MethodHandle #6:#49 #49 = Methodref #8.#59 #59 = NameAndType #28:#14
private static void lambda$main$0(); descriptor: ()V flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC Code: stack=2, locals=0, args_size=0 0: getstatic #5 3: ldc #6 5: invokevirtual #7 8: return LineNumberTable: line 18: 0 }
|
CONSTANT_MethodHandle_info结构包含两项信息,其结构如下所示:
1 2 3 4 5
| CONSTANT_MethodHandle_info { u1 tag; u1 reference_kind; u2 reference_index; }
|
常量池 #36 作为 invokeDynamic 的参数传入的是 lambda 函数的实现逻辑。根据BootstrapMethods对应的#34可以找到此处lambda InvokeDynamic指令对应的引导方法是LambdaMetafactory.metafactory,其返还一个CallSite。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public static CallSite metafactory(MethodHandles.Lookup caller, String invokedName, MethodType invokedType, MethodType samMethodType, MethodHandle implMethod, MethodType instantiatedMethodType) throws LambdaConversionException { AbstractValidatingLambdaMetafactory mf; mf = new InnerClassLambdaMetafactory(caller, invokedType, invokedName, samMethodType, implMethod, instantiatedMethodType, false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY); mf.validateMetafactoryArgs(); return mf.buildCallSite(); }
|
到此处,基本了解了 lambda 表达式的底层实现,是基于 JVM invokedynamic指令实现的。在后续博客中,会继续深入。