永久代和元空間都是 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 版本下,對方法區的具體實現而已。這就好像,世界羽協規定羽毛球比賽必須要使用羽毛球拍(方法區),而中國羽毛球運動員,第一年使用的是紅雙喜牌的羽毛球拍(永久代),第二年使用的是李寧牌羽毛球拍(元空間)一樣。
那麼問題來了,永久代為什麼被元空間給替代了?
關於這個問題,官方在 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:上面的那段描述好像說的已經很清楚了,但又好像什麼也沒說。這就好比,我問你「為什麼要買車?」,你說「別人都買車了,所以我要買車」,但為什麼別人要買車?
上述給出了移除永久代的回答,但卻沒有給出背後的原因,那接下來我們就來討論一下,為什麼要移除永久代?以及為什麼要有元空間?
當使用永久代實現方法區時,永久代的最大容量受制於 PermSize 和 MaxPermSize 引數設定的大小,而這兩個引數的大小又很難確定,因為在程式執行時需要載入多少類是很難估算的,如果這兩個引數設定的過小就會頻繁的觸發 FullGC 和導致 OOM(Out of Memory,記憶體溢位)。
但是,當使用元空間替代了永久代之後,出現 OOM 的機率就被大大降低了,因為元空間使用的是本地記憶體,這樣元空間的大小就只和本地記憶體的大小有關了,從而大大降低了 OOM 的問題。
因為元空間使用的是本地記憶體,這樣就無需運維人員再去專門設定和調整元空間的大小了。
在 HotSpot 虛擬機器器中,方法區的實現經歷了以下 3 個階段:
永久代和元空間都是 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 常見面試題,持續更新...