jvm--常量池

class常量池、字符串常量池、運行時常量池java

 class常量池jvm

咱們寫的每個Java類被編譯後,就會造成一份class文件;class文件中除了包含類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池(constant pool table),用於存放編譯器生成的各類字面量(Literal)和符號引用(Symbolic References);每一個class 文件都有一個class常量池;優化

注:字面量包括:1.文本字符串 2.八種基本類型的值 3.被聲明爲final的常量等; 符號引用包括:1.類和方法的全限定名 2.字段的名稱和描述符 3.方法的名稱和描述符spa

例如String a = 「aa」。其中」aa」就是字面量3d


 字符串常量池對象

看名字咱們就能夠知道字符串常量池會用來存放字符串,也就是說常量池中的文本字符串會在類加載時進入字符串常量池。
那字符串常量池和運行時常量池是什麼關係呢?上面咱們說常量池中的字面量會在類加載後進入運行時常量池,其中字面量中有包括文本字符串,顯然從這段文字咱們能夠知道字符串常量池存在於運行時常量池中。也就存在於方法區中。
不過在周志明那本深刻java虛擬機中有說到,到了JDK1.7時,字符串常量池就被移出了方法區,轉移到了裏了。
那麼咱們能夠推斷,到了JDK1.7以及以後的版本中,運行時常量池並無包含字符串常量池,運行時常量池存在於方法區中,而字符串常量池存在於中。blog

(字符串池裏的內容是在類加載完成,通過驗證,準備階段以後在[1.7以後在堆中,1.7以前在運行時常量池也就是方法區]生成字符串對象實例,而後將該字符串對象實例的引用值存到string pool中(記住:string pool中存的是引用值而不是具體的實例對象,具體的實例對象是在[1.7以後在堆中,1.7以前在運行時常量池也就是方法區]開闢的一塊空間存放的。)。 在HotSpot VM裏實現的string pool功能的是一個StringTable類,它是一個哈希表,裏面存的是駐留字符串(也就是咱們常說的用雙引號括起來的)的引用(而不是駐留字符串實例自己),也就是說在[1.7以後在堆中,1.7以前在運行時常量池也就是方法區]的某些字符串實例被這個StringTable引用以後就等同被賦予了」駐留字符串」的身份。這個StringTable在每一個HotSpot VM的實例只有一份,被全部的類共享。)接口


運行時常量池內存

咱們知道類加載器會加載對應的Class文件,而上面的class文件中的常量池,會在類加載後進入方法區中的運行時常量池【此時存在在內存中】。由此可知,運行時常量池也是每一個類都有一個。字符串

注意運行時常量池存在於方法區中。class常量池中存的是字面量和符號引用,也就是說他們存的並非對象的實例,而是對象的符號引用值。而通過解析(resolve)以後,也就是把符號引用替換爲直接引用,解析的過程會去查詢字符串池,也就是咱們上面所說的StringTable(全局的),以保證運行時常量池所引用的字符串與全局字符串池中所引用的是一致的


 

 採用字面值的方式建立一個字符串時,JVM首先會去字符串池中查找是否存在"aaa"這個對象,若是不存在,則在字符串池中建立"aaa"這個對象,而後將池中"aaa"這個對象的引用地址返回給字符串常量str,這樣str會指向池中"aaa"這個字符串對象;若是存在,則不建立任何對象,直接將池中"aaa"這個對象的地址返回,賦給字符串常量。

在本例中,執行:str == str2 ,結果等於true, 這是由於,建立字符串對象str2時,字符串池中已經存在"aaa"這個對象,直接把對象"aaa"的引用地址返回給str2,這樣str2指向了池中"aaa"這個對象,也就是說str和str2指向了同一個對象,所以語句System.out.println(str == str2)輸出:true


 

 採用new關鍵字新建一個字符串對象時,JVM首先在字符串池中查找有沒有"aaa"這個字符串對象,若是有,則不在池中再去建立"aaa"這個對象了,直接在堆中建立一個"aaa"字符串對象,而後將堆中的這個"aaa"對象的地址返回賦給引用str3,這樣,str3就指向了堆中建立的這個"aaa"字符串對象;若是沒有,則首先在字符串池中建立一個"aaa"字符串對象,而後再在堆中建立一個"aaa"字符串對象,而後將堆中這個"aaa"字符串對象的地址返回賦給str3引用,這樣,str3指向了堆中建立的這個"aaa"字符串對象。

在這個例子中,執行:str3 == str4,結果等於false,由於,採用new關鍵字建立對象時,每次new出來的都是一個新的對象,也便是說引用str3和str4指向的是兩個不一樣的對象,所以語句System.out.println(str3 == str4)輸出:false。

字符串池的實現有一個前提條件:String對象是不可變的。由於這樣能夠保證多個引用能夠同事指向字符串池中的同一個對象。若是字符串是可變的,那麼一個引用操做改變了對象的值,對其餘引用會有影響,這樣顯然是不合理的。

注意:

  • 對於直接作+運算的兩個字符串(字面量)常量,jvm在編譯階段會對其進行優化,直接把運算後的結果放入常量池中,參與運算的字符串並不會放入常量池 
  • 對於先聲明的字符串字面量常量,會放入常量池,可是若使用字面量的引用進行運算就不會把運算後的結果放入常量池中了
相關文章
相關標籤/搜索