1. 字符串常量池
- Heap中,在编译阶段就把所有的字符串文字放进去
- 是一个StringTable类,它是一个哈希表,里面存的是字面值(也就是我们常说的用双引号括起来的)的引用(而不是字面值实例本身)
字符串常量池的位置
- 在JDK6.0及之前版本,字符串常量池是放在Perm Gen区(也就是方法区)中
- 在JDK7.0版本及之后,字符串常量池被移到了堆中
1.1 String str = new String(“abc”);
规范的类的创建过程,即在Java中,一切都是对象,而对象是类的实例,全部通过new()的形式来创建。
- 先定义一个名为str的对String类的对象引用变量放入栈中。
- 然后在堆中(不是常量池)创建一个指定的对象,并让str引用指向该对象。
- 在常量池中查找是否存在内容为"abc"字符串对象;如果不存在,则在常量池中创建内容为"abc"的字符串对象,并将堆中的对象与之联系起来;如果存在,则将new出来的字符串对象与字符串常量池中的对象联系起来(即让那个特殊的成员变量value的指针指向它)
延伸问题:String s = new String(“abc”);产生几个对象?答:一个或两个,如果常量池中原来没有”abc”,就是两个。
1.2 String str = “abc”;
- 先定义一个名为str的对String类的对象引用变量放入栈中。
- 在常量池(Heap中的)中查找是否存在内容为"abc"字符串对象。
- 如果不存在则在常量池中创建"abc",并让str引用该对象;如果存在则直接让str引用该对象。
String的 intern() 方法就是扩充常量池的一个方法;当一个String实例str调用intern()方法时,Java查找常量池中是否有相同的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用;
总结:
对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中;如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。
2. class文件常量池(静态常量池)
class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池,用于存放编译器生成的各种字面量和符号引用。字面量就是我们所说的常量概念,如字符串、被声明为final的常量值等。符号引用是一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可(它与直接引用区分一下,直接引用一般是指向方法区的本地指针,相对偏移量或是一个能间接定位到目标的句柄)。一般包括下面三类常量:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
它是Class文件中的内容,还不是运行时的内容,可以理解为Class文件中的字节码指令。
3. 运行时常量池
是方法区的一部分(运行时常量是相对于常量来说的)。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用。当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中