為什麼用元空間替代永久代?

2023-02-13 09:00:15

永久代和元空間都是 HotSpot 虛擬機器器中的概念,HotSpot 虛擬機器器是 Sun JDK 和 Open JDK 中自帶的虛擬機器器,也是目前使用範圍最廣泛的 Java 虛擬機器器,當我們提到虛擬機器器時,大概率指的就是 HotSpot 虛擬機器器。

但從《Java 虛擬機器器規範》的層面來說,並沒有所謂的「永久代」和「元空間」等區域,詳見官方檔案:https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-2.html。《Java 虛擬機器器規範》只是規定了一個區域叫「方法區(Method Area)」,而 「永久代」和「元空間」是 HotSpot 虛擬機器器在不同的 JDK 版本下,對方法區的具體實現而已。這就好像,世界羽協規定羽毛球比賽必須要使用羽毛球拍(方法區),而中國羽毛球運動員,第一年使用的是紅雙喜牌的羽毛球拍(永久代),第二年使用的是李寧牌羽毛球拍(元空間)一樣。

那麼問題來了,永久代為什麼被元空間給替代了?

1.官方答案

關於這個問題,官方在 JEP 122: Remove the Permanent Generation(移除永久代)中給出了答案,原文內容如下:

Motivation(動機)
This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.

以上內容翻譯成中文大意是:

這是 JRockit 虛擬機器器和 HotSpot 虛擬機器器融合工作的一部分。JRockit 客戶不需要設定永久層代(因為 JRockit 沒有永久代),所以要移除永久代。

JRockit 是 Java 官方收購的一家號稱史上執行最快的 Java 虛擬機器器廠商,之後 Java 官方在 JDK 8 時將 JRockit 虛擬機器器和 HotSpot 虛擬機器器進行了整合。

PS:JEP 是 JDK Enhancement Proposal 的縮寫,翻譯成中文是 JDK 改進提案。你也可以把它理解為 JDK 的更新檔案。

通過官方的描述,我們似乎找到了答案,也就是說,之所以要取消「永久代」是因為 Java 官方收購了 JRockit,之後在將 JRockit 和 HotSpot 進行整合時,因為 JRockit 中沒有「永久代」,所以把永久代給移除了

PS:上面的那段描述好像說的已經很清楚了,但又好像什麼也沒說。這就好比,我問你「為什麼要買車?」,你說「別人都買車了,所以我要買車」,但為什麼別人要買車?

2.背後的原因

上述給出了移除永久代的回答,但卻沒有給出背後的原因,那接下來我們就來討論一下,為什麼要移除永久代?以及為什麼要有元空間?

2.1 降低 OOM

當使用永久代實現方法區時,永久代的最大容量受制於 PermSize 和 MaxPermSize 引數設定的大小,而這兩個引數的大小又很難確定,因為在程式執行時需要載入多少類是很難估算的,如果這兩個引數設定的過小就會頻繁的觸發 FullGC 和導致 OOM(Out of Memory,記憶體溢位)。

但是,當使用元空間替代了永久代之後,出現 OOM 的機率就被大大降低了,因為元空間使用的是本地記憶體,這樣元空間的大小就只和本地記憶體的大小有關了,從而大大降低了 OOM 的問題。

2.2 降低運維成本

因為元空間使用的是本地記憶體,這樣就無需運維人員再去專門設定和調整元空間的大小了。

3.方法區發展史

在 HotSpot 虛擬機器器中,方法區的實現經歷了以下 3 個階段:

  1. JDK 1.6 及之前:方法區使用永久代實現,靜態變數存放在永久代
  2. JDK 1.7 :「去永久代」的前置版本,還存在永久代,不過已經將字串常數池和靜態變數從永久代移到了堆上
  3. JDK 1.8 及以後:無永久代,使用元空間(存放在本地記憶體中)實現方法區,常數儲存在元空間,但字串常數池和靜態變數依然儲存在堆中

總結

永久代和元空間都是 HotSpot 虛擬機器器對《Java 虛擬機器器規範》中方法區的實現,在 JDK 1.8 之前 HotSpot 是使用永久代來實現方法區的,但這樣會導致 JVM 調優比較困難,且容易發生 OOM 的問題,而 JDK 1.8 及之後,使用的是元空間存放在本地記憶體中的方式來替代永久代的,這樣就降低了 OOM 發生的可能性,也是 JRockit 和 HotSpot 融合之後的改動之一。

參考 & 鳴謝

openjdk.org/jeps/122

本文已收錄到 Gitee 開源倉庫《Java 面試指南》,其中包含的內容有:Redis、JVM、並行、並行、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、設計模式、訊息佇列等模組。Java 面試有它就夠了:超全 Java 常見面試題,持續更新...