什么是 invokedynamic 以及如何使用它?
我不断听说 JVM 中添加了所有新的很酷的功能,其中一个很酷的功能就是 invokedynamic。我想知道它是什么以及它如何使 Java 中的反射编程变得更容易或更好?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
我不断听说 JVM 中添加了所有新的很酷的功能,其中一个很酷的功能就是 invokedynamic。我想知道它是什么以及它如何使 Java 中的反射编程变得更容易或更好?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(5)
它是一个新的 JVM 指令,允许编译器生成代码,该代码调用比以前可能更宽松的规范的方法 - 如果您知道“这里是一篇非常精彩的博客文章,提供了很多细节。
It is a new JVM instruction which allows a compiler to generate code which calls methods with a looser specification than was previously possible -- if you know what "duck typing" is, invokedynamic basically allows for duck typing. There's not too much you as a Java programmer can do with it; if you're a tool creator, though, you can use it to build more flexible, more efficient JVM-based languages. Here is a really sweet blog post that gives a lot of detail.
作为我的 Java Records 文章的一部分,我阐述了Invoke Dynamic 背后的动机。让我们从印地的粗略定义开始。
介绍 Indy
Invoke Dynamic(也称为 Indy)是 JSR 292 旨在增强 JVM 对动态类型语言的支持。在 Java 7 中首次发布之后,
invokedynamic
操作码及其java.lang.invoke
包被 JRuby 等基于 JVM 的动态语言广泛使用。尽管 indy 专门设计用于增强动态语言支持,但它提供的功能远不止于此。事实上,它适合在任何语言设计者需要任何形式的动态性的地方使用,从动态类型杂技到动态策略!
例如,Java 8 Lambda 表达式实际上是使用
invokedynamic
实现的,尽管 Java 是一种静态类型语言!用户自定义字节码
很长一段时间以来,JVM确实支持四种方法调用类型:
invokestatic
调用静态方法,invokeinterface
调用接口方法,invokespecial< /code> 调用构造函数、
super()
或私有方法,invokevirtual
调用实例方法。尽管存在差异,这些调用类型有一个共同的特征:我们无法用自己的逻辑来丰富它们。相反,
invokedynamic
使我们能够以任何我们想要的方式引导调用过程。然后 JVM 负责直接调用 Bootstrapped 方法。Indy 是如何工作的?
JVM 第一次看到
invokedynamic
指令时,它会调用一个名为 的特殊静态方法引导方法。 bootstrap 方法是我们编写的一段 Java 代码,用于准备实际要调用的逻辑:然后 bootstrap 方法返回
java.lang.invoke.CallSite 的实例
。此CallSite
保存对实际方法的引用,即MethodHandle
。从现在开始,每次 JVM 再次看到此
invokedynamic
指令时,它都会跳过慢速路径并直接调用底层可执行文件。除非发生变化,否则 JVM 会继续跳过慢速路径。示例:Java 14 Records
Java 14
Records
提供了一种很好的紧凑语法来声明应该是哑数据持有者的类。考虑这个简单的记录:
此示例的字节码类似于:
在其 Bootstrap 方法表 中
: href="https://github.com/openjdk/jdk/blob/827e5e32264666639d36990edd5e7d0b7e7c78a9/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java#L338" rel="noreferrer">引导 方法
bootstrap
位于java.lang.runtime.ObjectMethods
类中。如您所见,此引导方法需要以下参数:MethodHandles.Lookup
实例(
Ljava/lang/invoke/MethodHandles$Lookup
部分)。toString
、equals
、hashCode
等)即将链接。例如,当值为
toString
时,bootstrap将返回一个
ConstantCallSite
(一个永远不会改变的CallSite
)指向此特定的实际
toString
实现记录。
TypeDescriptor
(Ljava/lang/invoke/TypeDescriptor
部分)。
Class
,表示Record 类类型。它是在这种情况下,
Class
。min;max
。MethodHandle
。这样bootstrap方法就可以根据此特定组件创建一个
MethodHandle
方法实施。
invokedynamic
指令将所有这些参数传递给引导方法。 Bootstrap 方法又返回ConstantCallSite
的实例。此ConstantCallSite
保存对所请求方法实现的引用,例如toString
。为什么是印地?
与反射 API 相比,
java.lang.invoke
API 非常高效,因为 JVM 可以完全看穿所有调用。因此,只要我们尽可能避开慢速路径,JVM可能会应用各种优化!除了效率方面的争论之外,由于 ,
invokedynamic
方法更可靠且更不易损坏。它的简单性。此外,为 Java Records 生成的字节码与属性的数量无关。因此,字节码更少,启动时间更快。
最后,我们假设新版本的 Java 包含一个新的且更高效的引导方法实现。通过
invokedynamic
,我们的应用可以利用这一改进而无需重新编译。这样我们就有了某种向前二进制兼容性。另外,这就是我们正在讨论的动态策略!其他示例
除了 Java 记录之外,动态调用 还用于实现以下功能:
LambdaMetafactory< /code>
StringConcatFactory
As part of my Java Records article, I articulated about the motivation behind Invoke Dynamic. Let's start with a rough definition of Indy.
Introducing Indy
Invoke Dynamic (Also known as Indy) was part of JSR 292 intending to enhance the JVM support for Dynamic Type Languages. After its first release in Java 7, The
invokedynamic
opcode along with itsjava.lang.invoke
luggage is used quite extensively by dynamic JVM-based languages like JRuby.Although indy specifically designed to enhance the dynamic language support, it offers much more than that. As a matter of fact, it’s suitable to use wherever a language designer needs any form of dynamicity, from dynamic type acrobatics to dynamic strategies!
For instance, the Java 8 Lambda Expressions are actually implemented using
invokedynamic
, even though Java is a statically typed language!User-Definable Bytecode
For quite some time JVM did support four method invocation types:
invokestatic
to call static methods,invokeinterface
to call interface methods,invokespecial
to call constructors,super()
or private methods andinvokevirtual
to call instance methods.Despite their differences, these invocation types share one common trait: we can’t enrich them with our own logic. On the contrary,
invokedynamic
enables us to Bootstrap the invocation process in any way we want. Then the JVM takes care of calling the Bootstrapped Method directly.How Does Indy Work?
The first time JVM sees an
invokedynamic
instruction, it calls a special static method called Bootstrap Method. The bootstrap method is a piece of Java code that we’ve written to prepare the actual to-be-invoked logic:Then the bootstrap method returns an instance of
java.lang.invoke.CallSite
. ThisCallSite
holds a reference to the actual method, i.e.MethodHandle
.From now on, every time JVM sees this
invokedynamic
instruction again, it skips the Slow Path and directly calls the underlying executable. The JVM continues to skip the slow path unless something changes.Example: Java 14 Records
Java 14
Records
are providing a nice compact syntax to declare classes that are supposed to be dumb data holders.Considering this simple record:
The bytecode for this example would be something like:
In its Bootstrap Method Table:
So the bootstrap method for Records is called
bootstrap
which resides in thejava.lang.runtime.ObjectMethods
class. As you can see, this bootstrap method expects the following parameters:MethodHandles.Lookup
representing the lookup context(The
Ljava/lang/invoke/MethodHandles$Lookup
part).toString
,equals
,hashCode
, etc.) the bootstrapis going to link. For example, when the value is
toString
, bootstrapwill return a
ConstantCallSite
(aCallSite
that never changes) thatpoints to the actual
toString
implementation for this particularRecord.
TypeDescriptor
for the method (Ljava/lang/invoke/TypeDescriptor
part).
Class<?>
, representing the Record class type. It’sClass<Range>
in this case.min;max
.MethodHandle
per component. This way the bootstrap method cancreate a
MethodHandle
based on the components for this particularmethod implementation.
The
invokedynamic
instruction passes all those arguments to the bootstrap method. Bootstrap method, in turn, returns an instance ofConstantCallSite
. ThisConstantCallSite
is holding a reference to requested method implementation, e.g.toString
.Why Indy?
As opposed to the Reflection APIs, the
java.lang.invoke
API is quite efficient since the JVM can completely see through all invocations. Therefore, JVM may apply all sorts of optimizations as long as we avoid the slow path as much as possible!In addition to the efficiency argument, the
invokedynamic
approach is more reliable and less brittle because of its simplicity.Moreover, the generated bytecode for Java Records is independent of the number of properties. So, less bytecode and faster startup time.
Finally, let’s suppose a new version of Java includes a new and more efficient bootstrap method implementation. With
invokedynamic
, our app can take advantage of this improvement without recompilation. This way we have some sort of Forward Binary Compatibility. Also, That’s the dynamic strategy we were talking about!Other Examples
In addition to Java Records, the invoke dynamic has been used to implement features like:
LambdaMetafactory
StringConcatFactory
前段时间,C# 添加了一个很酷的功能,C# 中的动态语法,
可以将其视为反射方法调用的语法糖。它可以有非常有趣的应用。请参阅http://www.infoq.com/presentations/Statically-Dynamic- Typing-Neal-Gafter
Neal Gafter,负责 C# 的动态类型,刚刚从 SUN 叛逃到 MS。因此,认为 SUN 内部也讨论过同样的事情并非没有道理。
我记得不久之后,一些 Java 家伙宣布了类似的东西,
不幸的是,这个功能在 Java 7 中找不到。非常失望。对于 Java 程序员来说,他们没有简单的方法在程序中利用
invokedynamic
。Some time ago, C# added a cool feature, dynamic syntax within C#
Think of it as syntax sugar for reflective method calls. It can have very interesting applications. see http://www.infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter
Neal Gafter, who's responsible for C#'s dynamic type, just defected from SUN to MS. So it's not unreasonable to think that the same things had been discussed inside SUN.
I remember soon after that, some Java dude announced something similar
Unfortunately, the feature is no where to be found in Java 7. Very disappointed. For Java programmers, they have no easy way to take advantage of
invokedynamic
in their programs.在继续调用dynamic之前,有两个概念需要理解。
1.静态与动态类型
静态 - 在编译时执行类型检查(例如 Java)
动态 - 在运行时执行类型检查(例如 JavaScript)
类型检查是一种验证程序类型安全的过程,即检查类和实例变量、方法参数、返回值和其他变量的类型信息。
例如,Java 在编译时就知道 int、String 等,而 JavaScript 中对象的类型只能在运行时确定
2。强类型与弱类型
强 - 指定对其操作提供的值类型的限制(例如 Java)
弱 - 转换(强制转换)操作的参数如果这些参数具有不兼容的类型(例如 Visual Basic)
知道 Java 是静态弱类型,如何在 JVM 上实现动态强类型语言?
invokedynamic 实现了一个运行时系统,可以在程序编译后选择最合适的方法或函数实现。
示例:
拥有 (a + b) 并且在编译时不知道有关变量 a,b 的任何信息,invokedynamic 会在运行时将此操作映射到 Java 中最合适的方法。例如,如果结果 a,b 是字符串,则调用 method(String a, String b)。如果结果 a,b 是 int,则调用 method(int a, int b)。
invokedynamic 是随 Java 7 引入的。
There are two concepts to understand before continuing to invokedynamic.
1. Static vs. Dynamic Typing
Static - preforms type checking at compile time (e.g. Java)
Dynamic - preforms type checking at runtime (e.g. JavaScript)
Type checking is a process of verifying that a program is type safe, this is, checking typed information for class and instance variables, method parameters, return values, and other variables.
E.g. Java knows about int, String,.. at compile time, while type of an object in JavaScript can only be determined at runtime
2. Strong vs. Weak typing
Strong - specifies restrictions on the types of values supplied to its operations (e.g. Java)
Weak - converts (casts) arguments of an operation if those arguments have incompatible types (e.g. Visual Basic)
Knowing that Java is a Statically and Weakly typed, how do you implement Dynamically and Strongly typed languages on the JVM?
The invokedynamic implements a runtime system that can choose the most appropriate implementation of a method or function — after the program has been compiled.
Example:
Having (a + b) and not knowing anything about the variables a,b at compile time, invokedynamic maps this operation to the most appropriate method in Java at runtime. E.g., if it turns out a,b are Strings, then call method(String a, String b). If it turns out a,b are ints, then call method(int a, int b).
invokedynamic was introduced with Java 7.
简而言之,invokedynamic 是 JVM 中的一个新操作码,在 JAVA 7 之前并不存在。
就反射而言,在此定义的上下文中:Java 反射是检查或修改运行时行为的过程运行时的类。,但是,我认为需要更多解释。
来自下面的文章:
引用自 使用invokedynamic理解Java方法调用
再说一遍,invokedynamic 是一个新的操作码,它允许 JAVA 中的新对象引用类型(Lambda)。
The short answer is invokedynamic is a new opcode in the JVM that didn't exist prior to JAVA 7.
As far as reflection, within the context of this definition: Java Reflection is a process of examining or modifying the run time behavior of a class at run time., however, I believe more explanation is needed.
From the article below:
Quoting from Understanding Java method invocation with invokedynamic
So again, invokedynamic is a new opcode that allows for a new object reference type in JAVA, a Lambda.