invokedynamic 是 lambda 高效运行的核心,它将方法绑定推迟到运行期:引导方法(如 lambdametafactory.metafactory)首次触发时生成适配类,调用点(callsite)缓存跳转目标,方法句柄指向编译生成的静态方法;相比匿名内部类预生成 class 文件,lambda 按需生成字节码、可复用类实例且支持 jvm 优化。

invokedynamic 是 Lambda 表达式能在 JVM 上高效运行的核心支撑,它把“方法调用的绑定”从编译期推迟到运行期,实现真正的动态链接。
它不是直接调用,而是先“找人再干活”
传统 invokestatic、invokevirtual 等指令在字节码里就写死了目标方法——编译时就必须确定。而 invokedynamic 只写了一个“占位符”,真正要执行哪个方法,留到第一次运行时才决定。
这个“找人”的过程由三部分协同完成:
-
引导方法(Bootstrap Method):JVM 首次遇到该 invokedynamic 指令时,自动调用它。Java 的标准实现是
LambdaMetafactory.metafactory; - 调用点(CallSite):引导方法返回的对象,相当于一个可变的“指针”,后续调用都通过它跳转;
-
方法句柄(MethodHandle):指向 Lambda 实际逻辑封装后生成的静态方法(如
lambda$main$0),并确保它能适配函数式接口的抽象方法签名。
Lambda 的代码如何变成可执行对象
你写 name -> "Hello, " + name,javac 不会生成 .class 文件,而是:
- 把 Lambda 主体编译成一个私有静态方法(无外部变量引用时)或实例方法(引用了 this 或成员变量);
- 在 main 方法字节码中插入 invokedynamic 指令,并在常量池关联引导方法和参数(比如接口类型、目标方法签名、实际逻辑方法句柄);
- 运行时首次触发该指令,引导方法检查上下文,动态生成一个实现了函数式接口的类(用 ASM 字节码技术),并创建其实例,绑定到 CallSite;
- 后续调用直接走已绑定的 CallSite,跳转到那个静态方法,完成执行。
为什么比匿名内部类更轻量
匿名内部类在编译期就生成独立的 .class 文件(如 OuterClass$1.class),每个不同逻辑都对应一个类,加载、验证、初始化开销大。
而 Lambda 借助 invokedynamic:
- 不预生成类文件,只在首次需要时按需生成字节码;
- 相同结构的 Lambda(如多个
() -> System.out.println("ok"))可能复用同一个生成类的实例; - JVM 可对 CallSite 做优化,比如将稳定调用点内联为普通方法调用,性能接近直接调用。
你可以把它看作一次“懒加载+智能代理”
编译器留下一个“待办事项”(invokedynamic 指令),JVM 运行时派一个“协调员”(引导方法)去现场组装工具(生成适配类)、指定工人(方法句柄)、装好开关(CallSite)。之后每次调用,只是按下那个开关,不重复组装。










