JVM 常量池
方法区
- 用于存储 jvm 加载的类的信息、常量、静态变量、编译后的代码
- jdk7 及以往版本,方法区基于永久代实现,理论上是堆的一部分
- jdk8 取消了永久代,在本地内存中划分区域-元空间,不属于 JVM 内存,方法区位于元空间中
- jdk8
graph LR
s(字符串常量池)--位于-->h(堆)
c(class文件常量池)--位于-->m(元空间)
r(运行时常量池)--位于-->m
- jdk7
graph LR
s(字符串常量池)--位于-->h(堆)
c(class文件常量池)--位于-->m(永久代)
r(运行时常量池)--位于-->m
- jdk6:
graph LR
s(字符串常量池)--位于-->p(永久代)
c(class文件常量池)--位于-->p
r(运行时常量池)--位于-->p
运行时常量池
- jdk7 仅仅把字符串常量池移动到了堆,jdk8 废弃了永久代变更为元空间,运行时常量池跟随元空间被移出JVM 内存,是方法区的一部分。
- 当类加载到内存中后,jvm 就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。并且在类加载的解析阶段会把运行时常量池的符号引用替换成直接引用,这个过程需要查找字符串常量池。
字符串常量池
-
本质上是一个哈希表,jdk6 中是固定大小的,jdk7 可以通过参数指定。
-
jdk6 及以往版本,是方法区的一部分,用于存放字符串对象。
-
jdk7 将字符串池放到了堆中,可以存放字符串对象,也可以存放字符串引用。
-
new String("abc")
,如果字符串常量池中已经存在 “abc”,则创建一个对象,如果没有,则先在字符串常量池创建“abc”,再在堆中创建一个拷贝对象因为其构造方法是
String(String str)
,括号内相当于双引号赋值方式创建字符串 -
字符串的 intern() 方法:
- jdk6
- 在池中查找,如果存在该串,返回该串引用。
- 如果不存在,将该串写入常量池,并返回引用。
- jdk7
- 在池中查找,如果存在该串,返回该串引用。
- 如果不存在,将该串的在堆中的引用存入常量池。
- jdk6
public static void main(String[] args)
{
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);//堆中和常量池中,地址不同
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);//jdk7 常量池中的引用和堆中相同
}
/**
打印结果是
jdk6 下false false
jdk7 下false true
**/
class 文件常量池
- class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池,用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)
- 是方法区的一部分,每个类都有一个 class 文件常量池。
- 字面量就是我们所说的常量概念,如文本字符串、被声明为final的常量值等。
- 符号引用是一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可,通常包含:类的全限定名、字段的名称和描述符、方法的名称和描述符。