赞
踩
常量池(Constant Pool),也叫 class 常量池(Class Constant Pool)。
java文件被编译成 class 文件,class 文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项就是常量池(Constant Pool),用于存放编译器生成的各种字面量( Literal )和 符号引用(Symbolic References)。
字面量( Literal ):就是我们所说的常量概念,如文本字符串、被声明为final的常量值等。
符号引用(Symbolic References):是一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。
符号引用一般包括下面三类常量:
描述符:是指描述字段或方法的类型的字符串。
常量池中数据项类型
常量池的每一项常量都是一个表,一共有如下表所示的11种各不相同的表结构数据,这每个表开始的第一位都是一个字节的标志位(如下所示),代表当前这个常量属于哪种常量类型。
序号 | 数据项类型 | 类型标志 | 类型描述 |
---|---|---|---|
1 | CONSTANT_Utf8 | 1 | UTF-8 编码的Unicode字符串 |
2 | CONSTANT_Integer | 3 | int 类型字面值 |
3 | CONSTANT_Float | 4 | float 类型字面值 |
4 | CONSTANT_Long | 5 | long 类型字面值 |
5 | CONSTANT_Double | 6 | double 类型字面值 |
6 | CONSTANT_Class | 7 | 对一个类或接口的符号引用 |
7 | CONSTANT_String | 8 | String 类型字面值 |
8 | CONSTANT_Fieldref | 9 | 对一个字段的符号引用 |
9 | CONSTANT_Methodref | 10 | 对一个类中声明的方法的符号引用 |
10 | CONSTANT_InterfaceMethodref | 11 | 对一个接口中声明的方法的符号引用 |
11 | CONSTANT_NameAndType | 12 | 对一个字段或方法的部分符号引用 |
字符串常量池(String Pool),即 String Literal Pool , 又叫全局字符串池。
在类加载完成,经过验证,准备阶段之后,在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到 String Pool 中。
String Pool 中存的是引用值,而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放。
在 HotSpot VM 里实现的 String Pool 功能的是一个 StringTable 类,它是一个哈希表,里面存的是 驻留字符的引用(而不是驻留字符串实例本身),也就是说在堆中的某些字符串实例被这个 StringTable 引用之后就等同被赋予了”驻留字符串”的身份。这个 StringTable 在每个 HotSpot VM 的实例只有一份,被所有的类共享。
jvm 在执行某个类的时候,必须经过加载、连接(验证、准备、解析)、初始化。而当类加载到内存中后,jvm 就会将 class 常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。
class 常量池中存的是字面量和符号引用,也就是说他们存的并不是对象的实例,而是对象的符号引用值。而经过解析之后,也就是把符号引用替换为直接引用,解析的过程会去查询字符串常量池 ,也就是 StringTable,以保证 运行时常量池所引用的字符串与 字符串常量池中所引用的是一致的。
举例说明:
public class StrTest {
public static void main(String []args) {
String str1 = "abc";
String str2 = new String("def");
String str3 = "abc";
String str4 = str2.intern();
String str5 = "def";
System.out.println(str1 == str3);//true
System.out.println(str2 == str4);//false
System.out.println(str4 == str5);//true
}
}
程序的内存分配过程:
class常量池、字符串常量池和运行时常量池的流转
可以看到在方法区里的 class 文件信息包括:魔数,版本号,常量池,类,父类和接口数组,字段,方法等信息,其实类里面又包括字段和方法的信息。
下面是原 java 代码 :
public class TestInt {
private String str = "hello";
void printInt(){
System.out.println(65535);
}
}
编译后得到 class 文件,经过反编译后得到如下信息:
可以看出被反编译的 class 文件中的内容和上面所说的是能对应上的。
class 文件常量池存储的是当 class 文件被 jvm 加载进来后存放在方法区的一些字面量和符号引用,字面量包括字符串、基本类型的常量。
运行时常量池是当 class 文件被加载完成后,jvm 会将 class 文件常量池里的内容转移到运行时常量池里,在 class 文件常量池的符号引用有一部分是会被转变为直接引用的,比如说类的静态方法或私有方法,实例构造方法,父类方法,这是因为这些方法不能被重写其他版本,所以能在加载的时候就可以将符号引用转变为直接引用,而其他的一些方法是在这个方法被第一次调用的时候才会将符号引用转变为直接引用的。
方法区里存储着 class 文件的信息和运行时常量池,class 文件的信息 包括类信息 和 class文件常量池 。
运行时常量池里的内容除了是 class 文件常量池里的内容外,还将 class 文件常量池里的符号引用转变为直接引用,而且运行时常量池里的内容是能动态添加的。例如调用 String 的 intern 方法就能将 string 的值添加到字符串常量池中,这里字符串常量池是包含在运行时常量池里的,但在 jdk1.8 后,将字符串常量池放到了堆中。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。