java 8 lambda 底层实现原理
之前有听说过 java 8 的 lambda 实现不再是java 1.5的匿名内部类语法糖。今天刚好有时间看看:
1 | public class App { |
经过编译后,生成了两个 class 文件:
1 | ls |
显然,App$LambdaDemo.class
是interface lambdaDemo的 class 文件。我们可以用命令查看下:
1 | $ javap -c -v App\$LambdaDemo |
接下来看看lambda 究竟是如何实现的:
1 | $ javap -c -v App |
注意看 main 方法的字节码,方法的 code 属性里:
1 | 0: invokedynamic #3, 0 // InvokeDynamic #0:runLambda:()Lcn/victor/study/App$LambdaDemo; |
lambda 的实现是通过 invokedynamic 指令来实现的。java虚拟机里调用方法的字节码指令有5种:
- invokestatic //调用静态方法
- invokespecial //调用私有方法、实例构造器方法、父类方法
- invokevirtual //调用实例方法
- invokeinterface //调用接口方法,会在运行时再确定一个实现此接口的对象
- invokedynamic //先在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法,在此之前的4条调用指令,分派逻辑是固化在java虚拟机内部的,而invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的。
invokedynamic指令是在jvm7中新增的,invokedynamic出现的位置代表一个动态调用点 invokedynamic指令后面会跟一个指向常量池的调用点限定符,这个限定符会被解析为一个动态调用点。调用点限定符的符号引用为CONSTANT_InvokeDynamic_info结构。
1 | CONSTANT_InvokeDynamic_info{ |
我们再看invokedynamic #3
中#3指向的线程池常量:
1 | #3 = InvokeDynamic #0:#37 // #0:runLambda:()Lcn/victor/study/App$LambdaDemo; |
CONSTANT_MethodHandle_info结构包含两项信息,其结构如下所示:
1 | CONSTANT_MethodHandle_info { |
常量池 #36 作为 invokeDynamic 的参数传入的是 lambda 函数的实现逻辑。根据BootstrapMethods对应的#34可以找到此处lambda InvokeDynamic指令对应的引导方法是LambdaMetafactory.metafactory,其返还一个CallSite。
1 | public static CallSite metafactory(MethodHandles.Lookup caller, |
到此处,基本了解了 lambda 表达式的底层实现,是基于 JVM invokedynamic指令实现的。在下篇博客,会深入理解invokedynamic相关的 java 动态类型实现逻辑(java.lang.invoke包)。