我们常常需要使用到String类型的字符串。作为最常用也是最基础的引用数据类型,JVM为String提供了字符串常量池来提高性能,本篇文章我们一起从底层JVM中认识并学习字符串常量池的概念和设计原理。
在日常开发过程中,字符串的创建是比较频繁的,而字符串的分配和其他对象的分配是类似的,需要耗费大量的时间和空间,从而影响程序的运行性能,所以作为最基础最常用的引用数据类型,Java设计者在JVM层面提供了字符串常量池。
为了提高性能并减少内存的开销,JVM在实例化字符串常量时进行了一系列的优化操作:
String str1 = "abc";
String str2 = "abc";
System.out.println("str1 == str2: " + (str1 == str2)); //结果:str1 == str2: true
提到字符串常量池,还得先从方法区说起。方法区和Java堆一样(但是方法区是非堆
),是各个线程共享的内存区域,是用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
很多人会把方法区称为永久代
,其实本质上是不等价的,只不过HotSpot虚拟机设计团队是选择把GC分代收集扩展到了方法区,使用永久代来代替实现方法区。其实,在方法区中的垃圾收集行为还是比较少的,这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载,但是这个区域的回收总是不尽如人意的,如果该区域回收不完全就会出现内存泄露。当然,对于JDK1.8
时,HostSpot VM对JVM模型进行了改造,将元数据放到本地内存
,将常量池和静态变量放到了Java堆
里。
JDK 1.8, HotSpot JVM将永久代移除了,使用本地内存来存储类的元数据信息,即为元空间(Metaspace)
所以,字符串常量池的具体位置是在哪里?当然这个我们后面需要区分jdk的版本,jdk1.7之前,jdk1.7,以及jdk1.8,因为这些版本中,字符串常量池因为方法区的改变而做了一些变化。
在jdk1.7之前,常量池是存放在方法区中的。
在jdk1.7中,字符串常量池移到了堆中,运行时常量池还在方法区中。
jdk1.8删除了永久代,方法区这个概念还是保留的,但是方法区的实现变成了元空间
,常量池沿用jdk1.7,还是放在了堆中。这样的效果就变成了:常量池和静态变量存储到了堆中,类的元数据及运行时常量池存储到元空间中。
为啥要把方法区从JVM内存(永久代)移到直接内存(元空间)?主要有两个原因:
本地IO操作——>直接内存操作——>非直接内存操作——>直接内存操作——>本地IO操作
,而直接内存操作:本地IO操作——>直接内存操作——>本地IO操作
。OutOfMemoryError
问题;而直接内存(元空间)是受到本地机器内存的限制,不会有这种问题。 String str1 = "123";
String str2 = "123";
String str3 = "123";
String str4 = new String("123");
String str5 = new String("123");
String str6 = new String("123");
结果:
str1 == str2:true
str2 == str3:true
str3 == str4:false
str4 == str5:false
str5 == str6:false
对于jvm底层,String str = new String("123")
创建对象流程是什么?
注意:
若常量池里没有"123"字符串,则创建了2个对象;若有该字符串,则创建了一个对象及对应的引用。
分析:若字符串常量池该字符串对象
分析:若字符串常量池没有该字符串对象
分析:若字符串常量池该字符串对象
分析:若字符串常量池该字符串对象
分析:若字符串常量池该字符串对象
分析:若字符串常量池该字符串对象
String str1 = "ab";
String str2 = "cd";
String str3 = str1 + str2;
通过intern()
方法。
代码
String str1 = "123";
String str2 = new String("123");
String str3 = str2;
System.out.println("str1 == str2:" + (str1 == str2));
System.out.println("str1 == str3:" + (str1 == str3));
//通过java.lang.String.intern()方法指定字符串对象
String str4 = str2.intern();
System.out.println("str1 == str4:" + (str1 == str4));
结果:
str1 == str2:false
str1 == str3:false
str1 == str4:true