invokedynamic
指令用于帮助 VM 在运行时确定方法引用,而不是在编译时对其进行硬接线。
这对于在运行时才知道确切方法和参数类型的动态语言很有用。但 Java lambda 并非如此。它们被转换为具有明确定义的参数的静态方法。并且可以使用 invokestatic
调用此方法。
那么 invokedynamic
对 lambda 有什么需求,尤其是在性能受到影响的情况下?
最佳答案
Lambda 不是使用 invokedynamic
调用的,它们的对象表示是使用 invokedynamic
创建的,实际调用是常规的 invokevirtual
或 调用接口(interface)
.
例如:
// creates an instance of (a subclass of) Consumer
// with invokedynamic to java.lang.invoke.LambdaMetafactory
something(x -> System.out.println(x));
void something(Consumer<String> consumer) {
// invokeinterface
consumer.accept("hello");
}
任何 lambda 都必须成为某个基类或接口(interface)的实例。该实例有时会包含从原始方法捕获的变量的副本,有时还会包含指向父对象的指针。 这可以实现为匿名类。
为什么调用dynamic
简短的回答是:在运行时生成代码。
Java 维护人员选择在运行时生成实现类。
这是通过调用 java.lang.invoke.LambdaMetafactory.metafactory
来完成的。
由于该调用的参数(返回类型、接口(interface)和捕获的参数)可以更改,因此需要 invokedynamic
。
使用 invokedynamic
在运行时构造匿名类,允许 JVM 在运行时生成该类字节码。对同一语句的后续调用使用缓存版本。使用 invokedynamic
的另一个原因是能够在将来更改实现策略,而无需更改已编译的代码。
未走的路
另一个选项是编译器为每个 lambda 实例创建一个内部类,相当于将上面的代码翻译成:
something(new Consumer() {
public void accept(x) {
// call to a generated method in the base class
ImplementingClass.this.lambda$1(x);
// or repeating the code (awful as it would require generating accesors):
System.out.println(x);
}
);
这需要在编译时创建类,然后在运行时加载。 jvm 工作这些类的方式将驻留在与原始类相同的目录中。第一次执行使用该 lambda 的语句时,必须加载和初始化该匿名类。
关于性能
第一次调用 invokedynamic
将触发匿名类生成。然后操作码 invokedynamic
被替换为 code这在性能上等同于手动编写匿名实例化。
关于java - 为什么使用invokedynamic 调用Java 8 lambda?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30002380/