2011-04-05 22 views
19

在非常简单的HelloWorld应用程序上运行javap时,我对常量池周围的输出存在一些疑惑。了解常量池的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行,我们看到了推动的 “Hello World” 的恒通过#22进入堆栈,但const#23似乎保存了实际值。我想我对#(数字)出现在打印输出右侧时的含义有些困惑。

Oracle/Sun's man page for javap还有很多不足之处。

回答

22

你所有的classinterfacefield名称和string常数进入了Java 常量池

作为每个VM规格(http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html):

的constant_pool是 结构(§4.4)表示各种 字符串常量,类和接口 名,字段名和其它 常数的表在 的ClassFile结构及其 子结构中被引用。每个 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处。

+0

截至'jdk-7'(http://bugs.sun.com/view_bug.do?bug_id=6868539)它是'utf8'而不是'asciz' – Eugene 2017-11-21 17:06:56

3

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

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

5

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