你還在看《深入理解Java虛擬機器》的執行時資料模型嗎?

llldddbbb發表於2019-03-31

學習JVM必看的書籍無疑是《深入理解Java虛擬機器》這本書了,在書中,關於執行時資料區域模型是這樣描述的:

image.png

在這裡我們只針對HotSpot VM來說,它是OracleJDK和OpenJDK中所帶的虛擬機器,也是目前使用範圍最廣的Java虛擬機器。在JDK7之前,這樣的模型是正確的。但是到了JDK8,如圖示紅的部分,做了一些優化。

什麼是方法區,什麼是永久代,執行時常量池又是什麼

  • “方法區”(Method Area),是執行緒共享的區域,用於儲存已被虛擬機器載入的類資訊,常量,靜態變數等資料。首先我們要知道,方法區是JVM的一種規範,是一個概念,而這個方法區的具體實現由各個虛擬機器廠商去實現。
  • “永久代”(Permanent Generation)就是HotSpot虛擬機器對於方法區的實現,也僅僅是針HotSpot才有的。
  • “執行時常量池”是方法區的一部分。用於存放編譯期生成的各種字面量和符號引用。其特性是具備動態性。

優化一:字串常量池從永久代劃到Java堆

由於常量池具備動態性,在程式執行過程中會有大量的字串常量在執行時常量池裡產生,此時如果放在永久代,則無法恰當的設定永久代的大小,容易出現效能問題和記憶體溢位。下面一個例子證明在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); 
    }
}
複製程式碼

結果如下:

image.png

證明 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());
        }
    }
複製程式碼

結果如下:

image.png

 我們看到這時報的是Java堆空間記憶體溢位,說明字串常量池是在堆中,注意,此時僅僅是字串常量池轉移到了堆中,但是執行時常量池依舊還是在方法區裡

優化二:移除了永久代,引入“元空間”(Metaspace)

為什麼移除永久代?

  1. 方法區大小難以設定,容易發生記憶體溢位。永久代會存放Class的相關資訊,一般這些資訊在編譯期間就能確定大小。但是如果是在一些需要動態生成大量Class的應用中,如:Spring的動態代理、大量的JSP頁面或動態生成JSP頁面等,由於方法區的大小在一開始就要分配好,因此就能難確定大小,容易出現記憶體溢位
  2. GC複雜且效率低。方法區儲存了類的後設資料資訊和各種常量,它的記憶體回收目標理應當是對這些型別的解除安裝和常量的回收。但由於這些資料被類的例項引用,解除安裝條件變得複雜且嚴格,回收不當會導致堆中的類例項失去後設資料資訊和常量資訊。因此,回收方法區記憶體不是一件簡單高效的事情。
  3. 促進HotSpot JVM與JRockit VM的融合。JRockit沒有方法區,移除永久代可以促進HotSpot JVM與JRockit VM的融合。

什麼是元空間(Metaspace),為什麼引入元空間

元空間的本質和永久代類似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機器中,而是使用本地記憶體。因此,預設情況下,元空間的大小僅受本地記憶體限制

元空間的特點:

  1. 每個載入器有專門的儲存空間。
  2. 不會單獨回收某個類。
  3. 元空間裡的物件的位置是固定的。
  4. 如果發現某個載入器不再存活了,會把相關的空間整個回收。

總結

最終JVM(HotSpot)執行時資料區域模型如下:

image.png

相關文章