關注微信公衆號: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