为什么使用invokedynamic调用Java 8 lambdas? [英] Why are Java 8 lambdas invoked using invokedynamic?

查看:150
本文介绍了为什么使用invokedynamic调用Java 8 lambdas?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

invokedynamic 指令用于帮助VM在运行时确定方法引用,而不是在编译时硬连接它。



这对于动态语言很有用,其中确切的方法和参数类型在运行时才知道。但Java lambda不是这种情况。它们被转换为具有明确定义的参数的静态方法。并且可以使用 invokestatic 调用此方法。



那么的需要是什么? lambdas的invokedynamic ,特别是当性能下降时?

解决方案

不使用<调用Lambda code> invokedynamic ,它们的对象表示是使用 invokedynamic 创建的,实际的调用是常规 invokevirtual invokeinterface



例如:

  //使用invokedynamic创建一个(子类)Consumer 
//的实例到java.lang.invoke.LambdaMetafactory
something(x - > ; System.out.println(x));

void something(Consumer< String> consumer){
// invokeinterface
consumer.accept(hello);
}

任何lambda都必须成为某个基类或接口的实例。该实例有时包含从原始方法捕获的变量的副本,有时包含指向父对象的指针。
这可以作为匿名类实现。



为何选择invokedynamic



<简而言之:在运行时生成代码。



Java维护者选择在运行时生成实现类。
这是通过调用 java.lang.invoke.LambdaMetafactory.metafactory 来完成的。
由于该调用的参数(返回类型,接口和捕获的参数)可能会发生变化,因此需要 invokedynamic



使用 invokedynamic 在运行时构造匿名类,允许JVM在运行时生成该类字节码。对同一语句的后续调用使用缓存版本。使用 invokedynamic 的另一个原因是将来可以更改实现策略,而无需更改已编译的代码。



未走的路



另一个选项是编译器为每个lambda实例创建一个内部类,相当于翻译上面的内容代码转换为:

 某事(新消费者(){
public void accept(x){
/ /调用基类中的生成方法
ImplementingClass.this.lambda $ 1(x);

//或重复代码(因为它需要生成加法器而很糟糕):
System.out.println(x);
}
);

这需要在编译时创建类,然后在运行时加载。 jvm的工作方式这些类将与原始类位于同一目录中。并且第一次执行使用该lambda的语句时,必须加载并初始化该匿名类。



关于效果



第一次调用 invokedynamic 将触发匿名类生成。然后将操作码 invokedynamic 替换为代码在性能上与手动编写匿名实例化相当。


The invokedynamic instruction is used to help the VM determine the method reference at runtime instead hardwiring it at compile time.

This is useful with dynamic languages where the exact method and argument types aren't known until runtime. But that isn't the case with Java lambdas. They are translated to a static method with well defined arguments. And this method can be invoked using invokestatic.

So then what is the need of invokedynamic for lambdas, especially when there is a performance hit?

解决方案

Lambdas are not invoked using invokedynamic, their object representation is created using invokedynamic, the actual invocation is a regular invokevirtual or invokeinterface.

For example:

// 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"); 
}

Any lambda has to become an instance of some base class or interface. That instance will sometimes contain a copy of the variables captured from the original method and sometimes a pointer to the parent object. This can be implemented as an anonymous class.

Why invokedynamic

The short answer is: to generate code in runtime.

The Java maintainers chose to generate the implementation class in runtime. This is done by calling java.lang.invoke.LambdaMetafactory.metafactory. Since the arguments for that call (return type, interface, and captured parameters) can change, this requires invokedynamic.

Using invokedynamic to construct the anonymous class in runtime, allows the JVM to generate that class bytecode in runtime. The subsequent calls to the same statement use a cached version. The other reason to use invokedynamic is to be able to change the implementation strategy in the future without having to change already compiled code.

The road not taken

The other option would be the compiler creating an innerclass for each lambda instantiation, equivalent to translating the above code into:

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);
    }
);   

This requires creating classes in compile time and having to load then during runtime. The way jvm works those classes would reside in the same directory as the original class. And the first time you execute the statement that uses that lambda, that anonymous class would have to be loaded and initialized.

About performance

The first call to invokedynamic will trigger the anonymous class generation. Then the opcode invokedynamic is replaced with code that's equivalent in performance to the writing manually the anonymous instantiation.

这篇关于为什么使用invokedynamic调用Java 8 lambdas?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆