理解 javap 命令输出的常量池内容

25

在运行一个非常简单的HelloWorld应用程序时,我对输出中的常量池有些困惑。

测试代码

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

Javap -c -verbose 输出(剪辑)

// 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
}

好的,所以在第三行中,我们可以看到通过 #22 将 "hello world" 常量推入堆栈,但是 const #23 似乎保存了实际值。我有点困惑当 #(数字) 出现在打印输出的右侧时表示什么意思。

Oracle/Sun 的 javap 手册 并不十分详尽。

3个回答

25
你所有的classinterfacefield名称和string常量都存储在Java常量池中。
根据VM规范(http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html):
常量池是一个结构表(§4.4),表示各种字符串常量、类和接口名称、字段名称和其他在ClassFile结构及其子结构中引用的常量。每个常量池表项的格式由其第一个“标签”字节指示。常量池表从1到constant_pool_count-1进行索引。
因此,在常量池方面,可以将以下内容视为:
const #22 = String      #23;    //  hello world
const #23 = Asciz       hello world;

#22位置(索引为22)的值是String类型,其值为空终止C字符串(Asciz),索引为23的位置上是hello world


jdk-7 开始(http://bugs.sun.com/view_bug.do?bug_id=6868539),它是 utf8 而不是 asciz - Eugene

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

4

池条目#22是一个java.lang.String对象。 条目#23是用于构建该字符串的字符数组。

Java VM规范是javap的“缺失手册”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接