bigywater.tech
Java · 334

LambdaMetafactory:运行时生成函数式实现

lambda 在字节码里到底是什么?为什么第一个 lambda 特别慢?捕获变量会影响对象数量吗?能不能手动调用 LambdaMetafactory?可序列化的 lambda 又多了什么?这篇用 javap、反射和计时逐项看。

3.03 ms
第一个 lambda 的创建耗时(之后的调用点约 0.15 ms)
true / false
非捕获 lambda 两次求值是否同一个对象 / 捕获 lambda 两次求值是否同一个对象
7.26 vs 7.24 ns
手动 metafactory 生成的 Function 与 javac 编出的 Integer::parseInt 的调用耗时

字节码

实测 · 本机执行输出
  合成的 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 条目数: 4

对象身份与生成的类

实测 · 本机执行输出
1 同一个非捕获 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;字段数:0

链接成本

实测 · 本机执行输出
2 第一个 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

转储生成的类

实测 · 本机执行输出
  转储出的类文件: []

手动调用 metafactory

实测 · 本机执行输出
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)

可序列化 lambda

实测 · 本机执行输出
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

分步演示

LambdaMetafactory
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12;计时是单次测量或自写循环取最小值,不是 JMH
    • 没有验证:JDK 27 的行为(尤其 -Djdk.internal.lambda.dumpProxyClasses 的替代)、lambda 在 CDS 归档中的预生成(只用 -Xshare:off 对比了第一个 lambda)、Kotlin / Scala 编译器的 lambda 实现、lambda 对 GC / 逃逸分析的影响、嵌套 lambda 的合成方法命名规则
    京ICP备2026067502号-1京公网安备11010502063487号