了解常量池的javap输出

时间:2011-04-05 01:48:14

标签: java java-bytecode-asm 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's man page for javap还有很多不足之处。

3 个答案:

答案 0 :(得分:23)

您的所有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,其值为空终止c string(Ascizhello world位于索引23处。

答案 1 :(得分:5)

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

答案 2 :(得分:4)

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

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