关注微信公众号:CodingTechWork,一块儿学习进步。java
在Java开发中不论是先后端交互的JSON串,仍是数据库中的数据存储,咱们经常须要使用到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,由于这些版本中,字符串常量池由于方法区的改变而作了一些变化。jvm
在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");
结果:spa
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