常量池分类
常量池大体可以分为:静态常量池,运行时常量池。
- 静态常量池 存在于class文件中。比如经常使用的javap -verbose中,常量池总是在最前面把?
- 运行时常量池 在class文件被加载进了内存之后,常量池保存在了
方法区堆中。通常说的常量池,指的是运行时常量池。所以呢,讨论的都是运行时常量池
class常量池 / 静态常量池
我们写的每一个Java类被编译后,就会形成一份class文件;class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种 字面量 (Literal)和 符号引用 (Symbolic References),每个class文件都有一个class常量池。
字面量
包括:
- 文本字符串
- 八种基本类型的值
- 被声明为final的常量等;
符号引用
包括:
- 类和方法的全限定名
- 字段的名称和描述符
- 方法的名称和描述符。
运行时常量池
运行时常量池存在于内存中,也就是class常量池被加载到内存之后的版本,是方法区的一部分。不同之处是:它的字面量可以动态的添加(String类的intern()),符号引用可以被解析为直接引用。
VM在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。而当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。在解析阶段,会把符号引用替换为直接引用,解析的过程会去查询字符串常量池,也就是我们下面要说的StringTable,以保证运行时常量池所引用的字符串与字符串常量池中是一致的。
字符串常量池
在JDK6.0及之前版本,字符串常量池存放在方法区中在JDK7.0版本以后,字符串常量池被移到了堆中了。(参考:https://zhuanlan.zhihu.com/p/52710835)
1 | String a = "abc"; |
字符串字面量赋值会直接存放在常量池,后续有相同的字符串字面量,会指向同一个地址
常量池字符串 + String字符串 :**+运算时**会进行动态调用,生成仍是String:
1 | String s7 = "H"; |
intern 方法,首先在常量池中查找是否存在相等的字符串,有则引用,无则添加到常量池
1 | String s5 = new String("Hello"); |
包装类常量池(缓存)
看包装类部分