lambda 在字节码里到底是什么?为什么第一个 lambda 特别慢?捕获变量会影响对象数量吗?能不能手动调用 LambdaMetafactory?可序列化的 lambda 又多了什么?这篇用 javap、反射和计时逐项看。
合成的 lambda 方法: ['private static void lambda$ser$2a0e5aa9$1();', 'private java.lang.Integer lambda$cap$1(int);', 'private static java.lang.Integer lambda$nonCap$0();']
invokedynamic 调用点:
0: invokedynamic #13, 0 // InvokeDynamic #0:get:()Ljava/util/function/Supplier;
2: invokedynamic #17, 0 // InvokeDynamic #1:get:(LDemo;I)Ljava/util/function/Supplier;
0: invokedynamic #20, 0 // InvokeDynamic #2:apply:()Ljava/util/function/Function;
0: invokedynamic #24, 0 // InvokeDynamic #3:run:()LDemo$SerRunnable;
129: invokedynamic #24, 0 // InvokeDynamic #3:run:()LDemo$SerRunnable;
BootstrapMethods:
0: #142 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/
Method arguments:
#118 ()Ljava/lang/Object;
#120 REF_invokeStatic Demo.lambda$nonCap$0:()Ljava/lang/Integer;
#123 ()Ljava/lang/Integer;
1: #142 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/
Method arguments:
#118 ()Ljava/lang/Object;
#124 REF_invokeVirtual Demo.lambda$cap$1:(I)Ljava/lang/Integer;
#123 ()Ljava/lang/Integer;
2: #142 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/
Method arguments:
#127 (Ljava/lang/Object;)Ljava/lang/Object;
#129 REF_invokeStatic java/lang/Integer.parseInt:(Ljava/lang/String;)I
#134 (Ljava/lang/String;)Ljava/lang/Integer;
3: #149 REF_invokeStatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/l
Method arguments:
#136 ()V
#137 REF_invokeStatic Demo.lambda$ser$2a0e5aa9$1:()V
#136 ()V
#140 5
#141 0
BootstrapMethods 条目数: 41 同一个非捕获 lambda 求值两次是同一个对象吗:true;捕获了变量的 lambda 求值两次:false;方法引用 Integer::parseInt 两次:true
类名:非捕获 Main$$Lambda/0x…(hidden=true,synthetic=true,接口 [java.util.function.Supplier])
捕获 Main$$Lambda/0x…(hidden=true,synthetic=true,接口 [java.util.function.Supplier])
方法引用 Main$$Lambda/0x…(hidden=true,synthetic=true,接口 [java.util.function.Function])
捕获 lambda 类的构造器参数:[Main, int];字段:[Main arg$1, int arg$2]
捕获 lambda 类声明的方法:[public Object get];修饰符 final
非捕获 lambda 类的构造器数:1;字段数:02 第一个 lambda(含链接)3.03 ms;第二个(不同调用点)0.153 ms;第三个 0.147 ms;对照:匿名内部类对象创建(类已加载后第一次)0.130 ms 同一个调用点反复求值 1000 次:共 0.25 ms(只链接一次) 同样的程序加 -Xshare:off(不使用类数据共享): 2 第一个 lambda(含链接)6.42 ms;第二个(不同调用点)0.154 ms;第三个 0.127 ms;对照:匿名内部类对象创建(类已加载后第一次)0.126 ms
转储出的类文件: []
3 手动 LambdaMetafactory.metafactory 把 Integer.parseInt 变成 Function:f.apply("123") = 123;对象 Main$$Lambda/0x…(hidden=true,synthetic=true,接口 [java.util.function.Function]);CallSite 类型 ConstantCallSite;目标句柄类型 ()Function
instantiatedMethodType 与实现不兼容:LambdaConversionException: Type mismatch for lambda argument 0: class java.lang.Integer is not convertible to class java.lang.String
接口方法类型参数个数不对:LambdaConversionException: Incorrect number of parameters for static method invokeStatic java.lang.Integer.parseInt:(String)int; 0 captured parameters, 0 functional interface method parameters, 1 implementation parameters
调用耗时:手动 metafactory 生成的 Function 7.26 ns;javac 编出的 Integer::parseInt 7.24 ns(sum 校验 2)4 可序列化 lambda(Runnable & Serializable):类声明了 writeReplace 方法:true;普通 lambda 有 writeReplace:false SerializedLambda:capturingClass=Main,implMethodName=lambda$main$9f09a77f$1,implMethodKind=6,functionalInterfaceClass=Main$SerRunnable,getCapturedArgCount=0 序列化后 506 字节;反序列化后运行: 可序列化的 lambda 运行了 序列化普通 lambda:NotSerializableException: Main$$Lambda/0x000000b801001800