Java 中的常量池包括静态常量池和运行时常量池
静态常量池指的是 class 文件中的常量池(class 文件会用一些字节来存储类名, 方法名, 常量, 字符串字面值等)
运行时常量池指的是 jvm 加载 class 文件后, 将 class 文件中的常量池载入到内存中
之前常量池保存在方法区中, 在 jdk8 中, 移除了方法区, 转而用 Metaspace 区域替代.
我们常说的常量池, 就是指方法区中的运行时常量池
在 Java 源代码中的每一个字面值字符串, 都会在编译成 class 文件阶段, 形成标志号为 8(CONSTANT_String_info)的常量表 . 当 JVM 加载 class 文件的时候, 会为对应的常量池建立一个内存数据结构, 并存放在方法区中. 同时 JVM 会自动为 CONSTANT_String_info 常量表中的字符串常量的字面值 在堆中创建新的 String 对象(intern 字符串对象 , 又叫拘留字符串对象). 然后把 CONSTANT_String_info 常量表的入口地址转变成这个堆中 String 对象的直接地址(常量池解析).
源代码中所有相同字面值的字符串常量只可能建立唯一 一个拘留字符串对象. 实际上 JVM 是通过一个记录了拘留字符串引用的内部数据结构来维持这一特性的. 在 Java 程序中, 可以调用 String 的 intern()方法来使得一个常规字符串对象成为拘留字符串对象.
(1)String s=new String("Hello world"); 编译成 class 文件后的指令(在 https://baike.baidu.com/item/myeclipse 中查看):
事实上, 在运行这段指令之前, JVM 就已经为 "Hello world" 在堆中创建了一个拘留字符串( 值得注意的是: 如果源程序中还有一个 "Hello world" 字符串常量, 那么他们都对应了同一个堆中的拘留字符串). 然后用这个拘留字符串的值来初始化堆中用 new 指令创建出来的新的 String 对象, 局部变量 s 实际上存储的是 new 出来的堆对象地址.
(2)String s="Hello world";
这跟 (1) 中创建指令有很大的不同, 此时局部变量 s 存储的是早已创建好的拘留字符串的堆地址.
当 JVM 加载 class 文件的时候, 会检查字符串 "Hello world" 在字符串常量池中是否存在, 如果已经存在, 则 s 直接指向字符串常量池中的 "Hello world", 如果不存在, 则将 "Hello world" 加入字符串常量池中, 并在堆中创建新的 String 对象(intern 字符串对象), 然后把常量池中 "abc" 的入口地址转变成堆中创建的 String 对象的地址, s 指向字符串常量池中的 "Hello world".(s 保存的是堆中的拘留字符串的内存地址)
- (百度百科常量池)
- (深入浅出 java 常量池)
来源: http://www.bubuko.com/infodetail-3039328.html