學習JVM必看的書籍無疑是《深刻理解Java虛擬機》這本書了,在書中,關於運行時數據區域模型是這樣描述的: java
在這裏咱們只針對HotSpot VM來講,它是OracleJDK和OpenJDK中所帶的虛擬機,也是目前使用範圍最廣的Java虛擬機。在JDK7以前,這樣的模型是正確的。可是到了JDK8,如圖標紅的部分,作了一些優化。性能
因爲常量池具有動態性,在程序運行過程當中會有大量的字符串常量在運行時常量池裏產生,此時若是放在永久代,則沒法恰當的設定永久代的大小,容易出現性能問題和內存溢出。下面一個例子證實在JDK8中,字符串常量池已經放在堆中:學習
String.intern()方法的做用是返回一個字符串引用,引用的是字符串常量池中的字符串(字面量),咱們先來驗證一下這個方法:優化
public class StringConstantsPoolTest {
public static void main(String[] args) {
String str = "abc"; // str存儲在常量池
String str2 = new String("abc"); // str2 存儲在堆中
System.out.println(str == str2); // 結果爲false ,堆中的引用並不等於常量池中的引用
str2 = str2.intern(); // 獲取str2在常量池中的引用
System.out.println(str == str2);
}
}
複製代碼
結果以下: spa
證實 String.intern()方法返回了一個在常量池中的引用。 下面驗證字符串常量池在堆中: 設置JVM參數:線程
-Xms10m -Xmx10m -XX:-UseGCOverheadLimit代理
public static void main(String[] args) {
List<String> list = new ArrayList();
int i = 0;
while(true){
list.add(String.valueOf(i++).intern());
}
}
複製代碼
結果以下: code
咱們看到這時報的是Java堆空間內存溢出,說明字符串常量池是在堆中,注意,此時僅僅是字符串常量池轉移到了堆中,可是運行時常量池依舊仍是在方法區裏cdn
元空間的本質和永久代相似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機中,而是使用本地內存。所以,默認狀況下,元空間的大小僅受本地內存限制。對象
元空間的特色:
最終JVM(HotSpot)運行時數據區域模型以下: