了解 javap 对常量池的输出



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

<<p> 测试代码/strong>
public class TestClass {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}

Javap -c -verbose output (snippped)

// 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手册页留下了很多需要改进的地方。

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

根据VM Spec (http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html):

constant_pool是一个表结构(§4.4)表示各种字符串常量、类和接口名称、字段名称和其他内部引用的常量类文件结构和它的子结构。每个的格式表示Constant_pool表项通过它的第一个"标签"字节。的Constant_pool表从1开始索引constant_pool_count-1。

所以在常量池中,如下所示可以看作:

const #22 = String      #23;    //  hello world
const #23 = Asciz       hello world;

#22(索引22)处的值类型为String,其值为空终止c字符串(ascii) hello world位于索引23

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

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

Java VM Spec是javap的"缺失手册"

相关内容

  • 没有找到相关文章

最新更新