常量池

常量池分类

常量池大体可以分为:静态常量池,运行时常量池。

  • 静态常量池 存在于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
2
3
String a = "abc";
String a2 = "abc";
String b = new String("abc");

字符串字面量赋值会直接存放在常量池,后续有相同的字符串字面量,会指向同一个地址

常量池字符串 + String字符串 :**+运算时**会进行动态调用,生成仍是String:

1
2
3
String s7 = "H";
String s8 = "ello";
String s9 = s7 + s8;

intern 方法,首先在常量池中查找是否存在相等的字符串,有则引用,无则添加到常量池

1
2
String s5 = new String("Hello");
String s6 = s5.intern();

包装类常量池(缓存)

看包装类部分