从常量池 Java 字节码访问值

Accessing values from Constant Pool Java Bytecode

我有以下代码:

    public static void f(){
        double i = 0.0;
        for(i = 0.0; i<100.0; i++){}   
    }

转换为以下字节码:

public static void f();
Code:
   0: dconst_0
   1: dstore_0
   2: dconst_0
   3: dstore_0
   4: dload_0
   5: ldc2_w        #2                  // double 100.0d
   8: dcmpg
   9: ifge          19
  12: dload_0
  13: dconst_1
  14: dadd
  15: dstore_0
  16: goto          4
  19: return

我对包含评论 //double 100.0d

的行感到困惑

我有点理解 ldc2_w 的作用。它从常量池中获取适当的常量。并且常量池在解析 Java 代码后包含一个定义的常量列表,对吗?

但是#2的目的是什么?怎么知道这里应该放几号呢?在在线教程中,我看到有人改用 #4?

这个操作数是什么意思?

#2 是该常量在常量池中的索引,即常量池中的第 2 个条目。注意常量池还有其他的入口,包括方法名、字符串等。

通常编译器将java代码翻译成字节码指令并同时添加任何需要的常量池条目。请参阅 JVM Specification. There are also bytecode manipulation frameworks like ASM and bytecode assemblers like Jasmin 中的常量池数据结构的详细信息,它完全从开发人员那里抽象出常量池填充。

But what is the purpose of #2? How would one know what number should go here? On an online tutorial I saw someone use #4 instead?

通常你不会。如果您正在编写字节码汇编,您通常依赖汇编程序为您填充常量池。

例如,如果你写

ldc2_w 100.0

在Jasmin或者Krakatau中,汇编器会自动为100.0建立一个常量池入口,然后填入合适的索引。可能是2,可能是4,可能是50000。这取决于常量池中还有什么,但你通常不关心那个。

Krakatau 汇编器还允许您手动指定常量池条目。所以如果你想确保你的替身在位置 2,你可以做

.const [2] = Double 100.0
ldc2_w [2]

然而,几乎没有任何理由这样做。这仅在您关心类文件的确切二进制布局时才有用。

至于为什么您会看到第 2 个,那是因为 Javap 就是这样。它用于快速检查字节码。如果你使用不同的工具,你会得到不同的结果。例如,除非您指定往返模式,否则 Krakatau 反汇编器只会打印出 ldc2_w 100.0