1.Class文件常量池
在Class文件中除了有类的版本、方法、字段等描述信息以外,还有一项信息是常量池,也是占用class文件空间最大的数据项。由于常量池中常量的数量是不一定的,所以常量池的入口会放置一项u2类型的数据项用于记录常量池中常量的数目。
常量池中主要存放两大类常量:字面量和符号引用。
字面量
字面量类似于Java语言中常说的常量的概念,包括字符串和被final修饰的常量值;
符号引用
- 类和接口的全限定名:例如String类,全限定名就是java/lang/String;
- 字段的名称和描述符:名称即代码中定义的变量名(仅包括成员变量和类变量),描述符(eg: int 变量描述符为I,int[] 为[I);
- 方法的名称和描述符:名称即代码中定义的方法名,描述符:按照先参数列表后返回值的顺序描述(eg:String::toString()的描述符为 ()Ljava/lang/String);
2.运行时常量池(Runtime constant pool)
我们上面所说的class文件常量池在类加载后会进入方法区中的运行时常量池中,并且需要注意的是运行时常量池是全局共享的,多个类共享一个运行时常量池。
值得一提的是,运行时常量池相对于class文件常量池的另一个特征就是具有动态性,Java语言并不要求常量只在编译时期产生。运行期也可能有新的常量放入常量池中(否则为什么叫运行时常量池呢,如果所有的常量都在编译时产生那么运行时常量池和class文件常量池又有什么区别)。典型的应用就是String::intern()。
在JDK1.6的时候,调用这个方法虚拟机会在字符串常量池在查找是否有与当前字符串相等(equals)的对象,如果有,则返回这个对象;如果没有,则会在字符串常量池中添加这个对象。注意,是把这个对象添加到字符串常量池。
在jdk1.7以后,调用这个方法虚拟机会在字符串常量池在查找是否有与当前字符串相等(equals)的对象,如果有,则返回这个对象;如果没有,则会在字符串常量池中添加这个对象的引用。注意,这个时候添加的是对象在堆中的引用。
3.字符串常量池(String constant pool)
见名知意,字符串常量池是用来存放字符串的也就是说class文件常量池中的文本字符串会在类加载时进入字符串常量池。那么字符串常量池和运行时常量池是什么关系呢?即运行时常量池逻辑上是包含字符串常量池的。然而,在jdk1.7以后,字符串常量池就被移出了方法区,放到了堆内存中。jdk1.8以后,永久代也被移除了,jvm使用元空间(Meta space)代替永久代成为方法区的具体实现。