了解Java字节码中的Invokedynamic指令及其对操作数堆栈的影响

问题描述 投票:0回答:1

我正在寻求澄清

invokedynamic
指令如何在 Java 字节码中运行。为了说明这一点,我准备了一个利用 lambda 表达式的简单 Java 代码片段,这通常会导致在编译时生成
invokedynamic
指令:

public class App{
    public static void main(String... args){
        Runnable r = () -> {};
        r.run();
    }
}

使用

javac
编译后,会生成以下字节码:

0: invokedynamic #7,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
5: astore_1
6: aload_1
7: invokeinterface #11,  1           // InterfaceMethod java/lang/Runnable.run:()V
12: return

据我了解,在

invokedynamic
指令之后,操作数堆栈应包含
CallSite
(引导方法的结果),随后将其存储到操作数堆栈中。然后,将这个
CallSite
存储到变量中并执行。

但是,

Wikipedia
上对 invokedynamic 方法的描述指出:

[调用动态指令]调用动态方法并将结果放入堆栈(可能为空);

这个说法,特别是“可能无效”让我很困惑。这是否意味着引导方法不一定会返回

CallSite
?尽管我尝试过,但我无法找到任何生成
invokedynamic
指令而不在操作数堆栈上放置任何值的 Java 代码片段。

有人可以澄清一下

invokedynamic
指令是否可以不将任何东西放入操作数堆栈上,包括
CallSite
?我已经查看了 StackOverflow 上的相关答案,例如 thisthisthis,但没有一个明确解决操作数堆栈的状态。此外,我试图找到一个字节码调试器来检查操作数堆栈状态,但无济于事。

有关此事的任何见解或指导将不胜感激。

java jvm bytecode invokedynamic
1个回答
0
投票

您误解了

invokedynamic
的作用。

invokedynamic
不会将
CallSite
推入堆栈。 invokedynamic指令的
操作数
指的是返回
CallSite
的方法。对于 lambda 表达式,这是
LambdaMetafactory.metafactory
方法。

invokedynamic
调用此
CallSite
返回方法 (
metafactory
),获取
CallSite
,然后调用
CallSite
指示的目标方法句柄。如果此方法句柄需要参数,则会弹出操作数堆栈上的内容。最后,调用该方法句柄的结果被压入堆栈。

在这种情况下,

metafactory
返回的调用站点引用一些不带参数的方法,并返回
Runnable
的实现。
invokedynamic
将这个
Runnable
推入堆栈。然后将其存储到本地变量中,从同一变量加载(
astore
aload
),并使用
run
调用其
invokeinterface
方法。

metafactory
需要一些参数,您可能想知道
invokedynamic
如何知道要传递哪些参数。其中一部分只是“内置”到
invokedynamic
,其余部分存储在类文件的
BootstrapMethods
属性中。

另一个例子,字符串连接也使用

invokedynamic
。这次,操作数将引用
StringConcatFactory
中的方法之一。该方法将返回一个
CallSite
表示某个将返回连接字符串的方法。

有关更多信息,请参阅 JVMS

© www.soinside.com 2019 - 2024. All rights reserved.