了解常量池的javap输出

在一个非常简单的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似乎保持实际值。 我想我对#(数字)在打印输出右侧出现时的含义有点困惑。

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

所有classinterfacefield名称和string常量都进入java 常量池

根据VM Spec( 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类型,其值为null终止c string( Asciz ) hello world位于索引23。

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

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

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