了解 Javap 在常量池中的输出

2022-09-01 19:02:21

在非常简单的HelloWorld应用程序上运行javap时,我对常量池周围的输出有些困惑。

测试代码

public class TestClass {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}

Javap -c -verbose output (snipped)

// Header + consts 1..22 snipped
const #22 = String      #23;    //  hello world
const #23 = Asciz       hello world;

public static void main(java.lang.String[]);
  Signature: ([Ljava/lang/String;)V
  Code:
   Stack=2, Locals=1, Args_size=1
   0:   getstatic       #16; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #22; //String hello world
   5:   invokevirtual   #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return
  // Debug info snipped
}

好吧,所以在第3行,我们看到“hello world”常量通过#22推送到堆栈上,但const #23似乎持有实际值。我想我对#(数字)出现在打印输出的右侧时的含义有点困惑。

Oracle/Sun 的 javap 手册页还有很多不足之处。


答案 1

所有 、 、 名称和常量都进入 java 常量池classinterfacefieldstring

根据虚拟机规格 ( http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html ):

constant_pool是一个结构表 (§4.4),表示 ClassFile 结构及其子结构中引用的各种字符串常量、类和接口名称、字段名以及其他常量。每个constant_pool表条目的格式由其第一个“标记”字节指示。constant_pool表的索引范围为 1 到 constant_pool_count-1。

因此,就恒定池而言,如下所示的内容可以被视为:

const #22 = String      #23;    //  hello world
const #23 = Asciz       hello world;

位于 #22(索引 22)的值是类型,其值为空终止的 c 字符串 (Asciz) 位于索引 23 处。Stringhello world


答案 2

Java 常量池在存储字符串时存储两种不同类型的条目。首先,它将字符串文本存储为 UTF-8 编码数据(此处为常量 #23)。其次,它还存储一个字符串条目(#22),指示应使用常量#23的内容来构造。我认为这样做的原因是JVM与每个类相关联一个“运行时常量池”,该池由给定常量的动态实现组成。对于字符串,这可以是对保存给定字符的暂存对象的引用。UTF-8 常量数据除了字符串文本(例如,命名字段和类)之外还有其他用途,因此这种额外的间接寻址似乎是分离关注点的合理方法。StringString


推荐