永久代和元空間都是 HotSpot 虛擬機器中的概念,HotSpot 虛擬機器是 Sun JDK 和 Open JDK 中自帶的虛擬機器,也是目前使用範圍最廣泛的 Java 虛擬機器,當我們提到虛擬機器時,大機率指的就是 HotSpot 虛擬機器。
但從《Java 虛擬機器規範》的層面來說,並沒有所謂的“永久代”和“元空間”等區域,詳見官方文件:https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-2.html#jvms-2.5。《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 個階段:
- JDK 1.6 及之前:方法區使用永久代實現,靜態變數存放在永久代;
- JDK 1.7 :“去永久代”的前置版本,還存在永久代,不過已經將字串常量池和靜態變數從永久代移到了堆上;
- 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 常見面試題,持續更新...