jvm dup指令的用例

问题描述 投票:5回答:3

Java字节码指令集提供various forms of dup instruction。我在理解这些指令和swap指令可能有用时遇到麻烦。编译时,哪些Java代码将根据这些指令生成字节码?

java jvm bytecode java-bytecode-asm
3个回答
3
投票

dup的变量可以在普通的Java代码中出现。

例如如this answer中所述,对象实例化通常使用dup,因为new Object()被编译为

new #n              // n referencing Class java.lang.Object in the pool
dup
invokespecial #m    // m referencing Method java.lang.Object.<init>()V

此外,intArray[expression]++被编译为

… (code pushing the results of intArray and expression)
dup2
iaload
iconst_1
iadd
iastore

还有一点幻想

public static long example3(Long[] array, int i, long l) {
    return array[i]=l;
}

编译为

   0: aload_0
   1: iload_1
   2: lload_2
   3: invokestatic  #3  // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
   6: dup_x2
   7: aastore
   8: invokevirtual #4  // Method java/lang/Long.longValue:()J
  11: lreturn

将数组类型更改为long[]会产生dup2_x2]的示例]

this Q&A中所述,javac从不使用swapnop(在当前实现中)。但是仅仅因为javac没有使用特定的指令,所以不能假设没有编译器使用它。

例如还有其他Java编译器,例如ECJ,但是可能存在由其他编程语言创建的类文件,或者已经是检测工具的结果,当您要在运行时检测代码时,这些文件就变得很重要。而且javac的未来版本也可以使用以前未使用过的指令,就像Java 8之前的Java代码未使用invokedynamic

This discussion指向一种情况,其中swap是合适的。当使用try-with-resource时,将生成代码,处理已捕获的异常时捕获的异常。当前的javac会将其(基本上)编译为

astore        n
aload         o
aload         n
invokevirtual #x // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V

其中o是保存已捕获的异常的旧变量,而n将是一个全新的变量,将其编译为]]

aload         o
swap
invokevirtual #x // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V

相反。因此,这些指令并不是永远不需要的奇异结构。当特定的代码生成器不使用它们时,这只是实现细节。

谈到仪器,同样重要的是要记住,不能保证ClassFileTransformer会收到与编译器产生的字节码完全相同的字节码。它可能是等效的字节码。

因此,最重要的是,如果要实现ClassFileTransformer,则应该准备处理每个合法字节码。

我不知道javac何时使用它,但是当我们生成代码时,我们经常使用DUP和SWAP。例如,如果您正在执行

的等效操作
x.setCharm(y);
x.setSpin(z);

然后您将加载x并立即对其进行DUP,因为调用第一种方法会将其从堆栈中取出,并且您想使用它两次。

SWAP在您执行类似操作时会派上用场

y = x.getCharm();
z.setCharm(y);

第一条指令将y留在堆栈顶部,然后堆栈z和SWAP,因此您现在在堆栈上具有正确的值来调用第二条指令。

dup的另一个常见用例是数组初始化。

考虑代码int[] a = new int[] {1, 2, 3}

iastore指令将整数存储到数组中。它在堆栈上需要三个值:对该数组的引用,该数组中的索引以及要存储的值,并且至关重要的是,它在调用后会弹出所有三个值:

arrayref,index和value从操作数堆栈中弹出。 Java Virtual Machine Specification

将以上示例转换为字节码的一种幼稚方式可能看起来像这样:

iconst_3
newarray       int
iconst_0               # array index
iconst_1               # value
iastore                # a[0] = 1
aload_1                # load array ref again
iconst_1
iconst_2
iastore                # a[1] = 2
aload_1                # load array ref yet again
iconst_2
iconst_3
iastore                # a[2] = 3
...
astore_1

注意,对于每条iastore指令,都会重新加载对数组的引用。

可以使用dup来避免。在将索引和值推入堆栈之前,我们在堆栈上复制数组引用,该数组引用将底部条目留给下一个iastore指令重新使用:

iconst_3
newarray       int
dup
iconst_0
iconst_1
iastore                # a[0] = 1
dup
iconst_1
iconst_2
iastore                # a[1] = 2
dup
iconst_2
iconst_3
iastore                # a[2] = 3
...
astore_1
        

3
投票

我不知道javac何时使用它,但是当我们生成代码时,我们经常使用DUP和SWAP。例如,如果您正在执行


0
投票

dup的另一个常见用例是数组初始化。

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