25

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

测试代码

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
}

好的,所以在第 3 行,我们看到通过 #22 将“hello world”常量推入堆栈,但 const #23 似乎保留了实际值。我想我对 #(number) 出现在打印输出右侧的含义有些困惑。

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

4

3 回答 3

26

您所有的class, interface,field名称和string常量都进入 java常量池

根据 VM 规范(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)是类型String,其值为空终止 c 字符串(Ascizhello world在索引 23 处。

于 2011-04-05T02:12:02.693 回答
5

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

于 2011-04-05T02:12:10.713 回答
4

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

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

于 2011-04-05T02:09:47.983 回答