Invoke Dynamic
Table of Contents
1. Invoke Dynamic
1.1. Overview
invokedynamic 是 java 1.7 添加一个新的 JVM OPCODE, 主要用来支持动态类型语言. 在 java 1.8 中, OpenJDK 使用 invokedynamic 来实现 lambda.
invokedynamic 要解决的主要问题是:
function (x) {
x.foo();
}
应该编译为什么样的 bytecode? x 的类型是未知的, 那么 `x.foo()` 应该编译成 invoke-?
invokedynamic 的思想是:
由于 x 的类型只有在运行时才能知道, 当需要确定 `x.foo` 如何 dispatch 时,把调用时相关的信息 (例如 x 对象, 字符串 "foo", foo 的 method type 或 signature) 作为参数传递给一个用户提供的 bootstrap 函数, 后者会返回一个 CallSite 对象, 包含了具体的对应于当前 callsite 的 target function (MethodHandle), 即由用户去实现具体的 dispatch.
- invokedynamic 调用时是由 bootstrap 函数决定具体的 dispatch
- bootstrap 函数是用户可以提供的, 给各种动态语言基于 JVM 的实现提供了灵活性, 它相当于 linker 中的 dl_resolve 函数
- invokedynamic 相当于 `userspace` 的 dispatch
1.2. invokedynamic 与 lambda
OpenJDK 中 lambda 的实现:
import java.util.function.Consumer; import java.util.ArrayList; public class playground { static void foo(Consumer<String> c) { c.accept("hello"); } public static void main(String[] args) { foo(v -> {int x = v.length();}); } }
对应的 bytecode 为:
public static void main(java.lang.String[]); 0: invokedynamic #4, 0 // InvokeDynamic #0:accept:()Ljava/util/function/Consumer; 5: invokestatic #5 // Method foo:(Ljava/util/function/Consumer;)V 8: return private static void lambda$main$0(java.lang.String); 0: aload_0 1: invokevirtual #6 // Method java/lang/String.length:()I 4: istore_1 5: return bootstrap method: 0: #28 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method arguments: #29 (Ljava/lang/Object;)V #30 invokestatic playground.lambda$main$0:(Ljava/lang/String;)V #31 (Ljava/lang/String;)V
与 类似, 首先 lambda 被编译为一个 static 函数, 然后由 invokedynamic 在运行时通过 metafactory 这个 bootstrap 函数提供了一个与 LambdaConsumer 类似的 functional interface 的实例 ((通过 ASM 框架)