字符串常量池
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文件后的指令(在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保存的是堆中的拘留字符串的内存地址)
https://www.cnblogs.com/syp172654682/p/8082625.html(深入浅出java常量池)