常量池與方法區以及又讀new String對象創建問題

又拿出這道String str1 = new String("abc");創建幾個對象的面試題梳理了一下常量池與方法區的關係,希望能把這兩者的關係通過這道面試題說明白

方法區是什麼

簡單說方法區用於存儲jvm加載的類的信息、常量、靜態變量、編譯後的代碼

方法區、永久代與元空間的關係

下文都以HotSpot來說明

首先方法區是JVM規範的說法,永久代、元空間是HotSpot用來實現方法區的兩個具體的實現

JDK1.8以前使用永久代Perm實現了JVM規範中的方法區

JDK1.8廢棄永久代,變更爲元空間,不是廢棄了方法區

永久代與元空間的區別是元空間不在虛擬機內存中,而使用本地內存,目的是爲了融合HotSpot與JRockit VM而做出的努力並且減少,並且由於這部分空間的GC效果難以令人滿意

上面我們就說清楚了方法區、永久代與元空間這幾個名詞之間的關係和區別,下面我們在來看三個常量池的關係和不同

常量池

字符串常量池

是一個哈希表(StringTable),裏面存的是駐留字符串的引用

字符串駐留:JVM 爲了提高性能會將能在編譯時期確定的字符串放在字符串駐留池的內存塊中,String a = "abc"; String b = new String("def");都在編譯時期能確定主流字符串"abc""def"

在堆中的字符串實例被這個哈希表引用之後就等同被賦予了”駐留字符串”的身份
在JVM中字符串常量池被所有類共享
字符串常量池在JDK1.7的版本從永久代移動到了堆

class文件常量池

class文件中除了包含類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池,用於存放編譯器生成的各種字面量(Literal)和符號引用(Symbolic References)。注意這是class文件中的內容
字面量就是我們所說的常量概念,如文本字符串、被聲明爲final的常量值等
符號引用是一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可,通常包含:類的全限定名、字段的名稱和描述符、方法的名稱和描述符
總之這些內容能表示這個類的代碼內容

運行時常量池

JDK1.7僅僅把字符串常量池移動到了堆,JDK1.8雖然廢棄了永久代變更爲元空間,但是運行時常量池仍然跟隨元空間被移出JVM內存
當類加載到內存中後,jvm就會將class常量池中的內容存放到運行時常量池中,由此可知,運行時常量池也是每個類都有一個
並且在類加載的解析階段會把運行時常量池的符號引用替換成直接引用,這個過程需要查找字符串常量池

由上面的內容我們可知new String對象創建問題只和堆以及堆內的字符串常量池有關係(字符串常量池在不在堆對new String沒什麼影響)

 1      //在堆中會有一個”abc”實例,全局StringTable中存放着”abc”的一個引用值
 2         String str1 = "abc";
 3         //生成兩個實例,一個是”def”的實例對象,並且StringTable中存儲一個”def”的引用值,還有一個是new出來的一個”def”的實例對象(指向str2)
 4         String str2 = new String("def");
 5         //查找StringTable,裏面有”abc”的全局駐留字符串引用,所以str3的引用地址與之前的那個已存在的相同
 6         String str3 = "abc";
 7         //調用intern()函數,返回StringTable中”def”的引用值,如果沒有就將str2的引用值添加進去
 8         String str4 = str2.intern();
 9         //最後str5在解析的時候就也是指向存在於StringTable中的”def”的引用值
10         String str5 = "def";
11         //生成一個實例,new出來的一個”def”的實例對象(指向str6),(def母本與StringTable放入”def”引用值已經創建)
12         String str6 = new String("def");
13 
14         System.out.println(str1 == str3);//true
15 
16         System.out.println(str2 == str4);//false
17         System.out.println(str4 == str5);//true
18 
19         System.out.println(str5 == str2);//false
20         System.out.println(str6 == str2);//false
21 
22         System.out.println(str6 == str2.intern());//false
23         System.out.println(str6.intern() == str2.intern());//true

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章