Java中几种常量池的区分
1. 全局字符串池(string pool)
string pool也有叫做string literal pool。
全局字符串池里的内容是在类加载完成,经过验证,准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到 string pool 中。string pool中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中存放的。
在HotSpot VM里实现的 string pool 功能的是一个 StringTable 类,里面存的是驻留字符串的引用,而不是驻留字符串实例本身,也就是说在堆中的某些字符串实例被这个 StringTable 引用之后就等同被赋予了”驻留字符串”的身份。
StringTable 在每个HotSpot VM 中只有一份,被所有的类共享,也就是全局字符串池中的”全局“含义。
2. class文件常量池(class constant pool)
class文件常量池是静态常量池。
class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量、符号引用:
-
字面量就是我们所说的常量概念,如文本字符串、被声明为final的常量值等。
-
符号引用是用一组符号来描述所引用的目标。符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可(符号引用与直接引用的区分:直接引用一般是指向方法区的本地指针,相对偏移量或是一个能间接定位到目标的句柄)。一般包括下面三类常量:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
常量池的每一项常量都是一个表,如下面是UTF-8编码的字符串的表结构:
CONSTANT_Utf8_info{
u1 tag;
u2 length;
u1 bytes[];
}
3. 运行时常量池(runtime constant pool)
运行时常量池是动态常量池。
当类加载到内存中后,JVM 会将class文件常量池中的内容存放到运行时常量池中。
运行时常量池是每个类都有一个。
class文件常量池中存的是字面量和符号引用,也就是说他们存的并不是对象的实例,而是对象的符号引用值。经过解析(resolve)之后,会把符号引用替换为直接引用,解析的过程会去查询全局字符串池(StringTable),以保证运行时常量池所引用的字符串与全局字符串池中所引用的是一致的。
4. 示例
String str1 = new String("def");
String str2 = str1.intern();
String str3 = "def";
System.out.println(str1 == str2);//false
System.out.println(str2 == str3);//true
在运行 String str2 = new String("def");
时会生成两个实例:一个是”def”的实例对象,并且在StringTable中存储一个该”def”对象的引用值;另一个是new出的一个”def”的实例对象,与第一个是不同的实例。
5. 总体过程
程序经过编译之后,在该类的class文件常量池中存放符号引用。类加载之后,将class文件常量池中存放的符号引用转存到运行时常量池中,经过验证,准备阶段之后,在堆中生成驻留字符串的实例对象,然后将这个对象的引用存到全局字符串池(String Pool)中,也就是StringTable中。最后在解析阶段,要把运行时常量池中的符号引用替换成直接引用,就直接查询StringTable,保证运行时常量池中的引用值与StringTable里的引用值一致。
网友评论