首先明确class常量池、運作時常量池、字元串常量池不是同一個概念。
常量池表(Constant Pool Table)
常量池表(Constant Pool Table)是Class檔案(位元組碼檔案)的一部分,在編譯階段,用于存放編譯期生成的各種字面量與符号引用,這部分内容将在類加載後存放到方法區的運作時常量池中(運作時常量池就是常量池在程式運作時的稱呼)。常量池,可以看做是一張表,虛拟機指令根據這張常量表找到要執行的類名,方法名,參數類型、字面量等資訊。
運作時常量池( Runtime Constant Pool)
- 運作時常量池( Runtime Constant Pool)是方法區的一部分。
- 在加載類和接口到虛拟機後,就會建立對應的運作時常量池。
- JVM為每個已加載的類型(類或接口)都維護一個常量池。池中的資料項像數組項一樣,是通過索引通路的。
- 運作時常量池中包含多種不同的常量,包括編譯期就已經明确的數值字面量,也包括到運作期解析後才能夠獲得的方法或者字段引用。此時不再是常量池中的符号位址了,這裡換為真實位址。
- 運作時常量池,相對于Class檔案常量池的另一重要特征是:具備動态性。
- 運作時常量池類似于傳統程式設計語言中的符号表(symbol table),但是它所包含的資料卻比符号表要更加豐富一些。
- 當建立類或接口的運作時常量池時,如果構造運作時常量池所需的記憶體空間超過了方法區所能提供的最大值,則JVM會抛OutofMemoryError異常。
字元串常量池 (StringTable)
- String table又稱為String pool,String的String Pool(字元串常量池)是一個固定大小的Hashtable,預設值大小長度是1009。如果放進String Pool的String非常多,就會造成Hash沖突嚴重,進而導緻連結清單會很長,而連結清單長了後直接會造成的影響就是當調用String.intern()方法時性能會大幅下降。
- 使用-XX:StringTablesize可設定StringTable的長度。
- 在JDK6中StringTable是固定的,就是1009的長度,是以如果常量池中的字元串過多就會導緻效率下降很快,StringTablesize設定沒有要求。
- 在JDK7中,StringTable的長度預設值是60013,StringTablesize設定沒有要求。
- 在JDK8中,StringTable的長度預設值是60013,StringTable可以設定的最小值為1009。
- 字元串常量池裡存的到底是什麼這裡先不做過多介紹了。
永久代演進過程
1.首先明确:隻有Hotspot才有永久代。BEA JRockit、IBMJ9等來說,是不存在永久代的概念的。原則上如何實作方法區屬于虛拟機實作細節,不受《Java虛拟機規範》管束,并不要求統一
2.Hotspot中方法區的變化:
JDK1.6及以前 | 有永久代(permanent generation),字元串常量池、靜态變量存儲在永久代上 |
---|---|
JDK1.7 | 有永久代,但已經逐漸 “去永久代”,字元串常量池,靜态變量移除,儲存在堆中 |
JDK1.8 | 無永久代,類型資訊,字段,方法,常量儲存在本地記憶體的元空間,但字元串常量池、靜态變量仍然在堆中。 |
JDK1.6及以前

JDK1.7
JDK1.8
Class常量池與運作時常量池示意圖:
總結
JDK1.6之前:
永久代(方法區)裡存儲着class檔案的資訊和運作時常量池(動态常量池),class檔案的資訊包括類資訊和class常量池(靜态常量池),運作時常量池(動态常量池)裡包含着字元串常量池。
JDK1.7:
永久代(方法區)裡存儲着class檔案的資訊和運作時常量池(動态常量池),class檔案的資訊包括類資訊和class常量池(靜态常量池),字元串常量池在堆中。
JDK1.8:
元空間裡存儲着class檔案的資訊和運作時常量池(動态常量池),class檔案的資訊包括類資訊和class常量池(靜态常量池),字元串常量池在堆中。