bigywater.tech
Java · 151

Lambda:invokedynamic 如何生成函数对象

Lambda 在 JVM 里到底是什么?这篇用 javap 看字节码、用对象比较和类信息看运行时形态、用类加载日志看首次链接的代价(方法引用的四种形式、LambdaMetafactory 的直接调用在后面的篇目)。

0 个类文件
lambda 编译后不产生独立的 .class(匿名内部类会)
true / false
不捕获变量的 lambda 求值多次是同一个对象 / 捕获变量时每次不同
约 3 ms
第一个 lambda 位置首次求值的耗时(含 invokedynamic 链接)

编译产物

实测 · 本机执行输出
  out 目录下的 .class 文件: ['Main$1.class', 'Main.class'] (匿名内部类 Main$1 有独立的 class 文件,lambda 没有)
  main 里的 invokedynamic 指令:
    0: invokedynamic #7, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
    6: invokedynamic #11, 0 // InvokeDynamic #1:apply:()Ljava/util/function/Function;
    16: invokedynamic #15, 0 // InvokeDynamic #2:run:(I)Ljava/lang/Runnable;
  编译器生成的私有合成方法:
    private static void lambda$main$2(int);
    private static java.lang.Integer lambda$main$1(java.lang.Integer);
    private static void lambda$main$0();
  BootstrapMethods 里的引导方法: ['metafactory'] (三个 lambda 都用 LambdaMetafactory.metafactory)

捕获与对象身份

实测 · 本机执行输出
  不捕获变量的 lambda,同一个位置求值 3 次:non[0]==non[1] true,non[1]==non[2] true
  捕获了变量 k 的 lambda,同一个位置求值 3 次:cap[0]==cap[1] false,cap[1]==cap[2] false
  两个写法完全相同、但在不同位置的 lambda:x==y false,x.getClass()==y.getClass() false

运行时的隐藏类

实测 · 本机执行输出
  getClass().getName() = Main$$Lambda/0x…
  isSynthetic = true,isHidden = true,实现的接口 = [interface java.lang.Runnable],父类 = java.lang.Object
  Class.forName(类名) → ClassNotFoundException(隐藏类不能按名字加载)
  声明的方法 = [run]

首次链接的代价

实测 · 本机执行输出
  第一个 lambda 位置第一次求值(含 invokedynamic 链接):3021 µs;第二个位置:149 µs(这里夹着 s1.get 的调用);第三个位置:143 µs
  同一个位置再求值 1000 次:平均 289 ns/次
  (另外两次独立运行:)
  2895 µs
  3163 µs
  加载的类数:什么都不做 441;只用 1 个 lambda 531(多 90 个);只用 1 次字符串 +(也是 invokedynamic)527(多 86 个);identity 模式(4 个 lambda 位置 + 字符串拼接)560(多 119 个)

分步演示

Lambda 与 invokedynamic
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12
    • 没有验证:-Djdk.internal.lambda.dumpProxyClasses 导出生成的类、LambdaMetafactory 的内部实现、JDK 17 与 27 的生成方式差异、CDS 归档里已有 lambda 形态的效果、序列化 lambda;首次求值耗时是 3 次运行的观察,不同机器不同
    京ICP备2026067502号-1京公网安备11010502063487号