為什么用元空間替代永久代?
永久代和元空間都是 HotSpot 虛擬機(jī)中的概念,HotSpot 虛擬機(jī)是 Sun JDK 和 Open JDK 中自帶的虛擬機(jī),也是目前使用范圍最廣泛的 Java 虛擬機(jī),當(dāng)我們提到虛擬機(jī)時(shí),大概率指的就是 HotSpot 虛擬機(jī)。
但從《Java 虛擬機(jī)規(guī)范》的層面來(lái)說(shuō),并沒(méi)有所謂的“永久代”和“元空間”等區(qū)域,詳見(jiàn)官方文檔:https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-2.html#jvms-2.5?!禞ava 虛擬機(jī)規(guī)范》只是規(guī)定了一個(gè)區(qū)域叫“方法區(qū)(Method Area)”,而“永久代”和“元空間”是 HotSpot 虛擬機(jī)在不同的 JDK 版本下,對(duì)方法區(qū)的具體實(shí)現(xiàn)而已。這就好像,世界羽協(xié)規(guī)定羽毛球比賽必須要使用羽毛球拍(方法區(qū)),而中國(guó)羽毛球運(yùn)動(dòng)員,第一年使用的是紅雙喜牌的羽毛球拍(永久代),第二年使用的是李寧牌羽毛球拍(元空間)一樣。
那么問(wèn)題來(lái)了,永久代為什么被元空間給替代了?
1.官方答案
關(guān)于這個(gè)問(wèn)題,官方在 JEP 122: Remove the Permanent Generation(移除永久代)中給出了答案,原文內(nèi)容如下:
Motivation(動(dòng)機(jī))
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.
以上內(nèi)容翻譯成中文大意是:
這是 JRockit 虛擬機(jī)和 HotSpot 虛擬機(jī)融合工作的一部分。JRockit 客戶(hù)不需要配置永久層代(因?yàn)?JRockit 沒(méi)有永久代),所以要移除永久代。
JRockit 是 Java 官方收購(gòu)的一家號(hào)稱(chēng)史上運(yùn)行最快的 Java 虛擬機(jī)廠商,之后 Java 官方在 JDK 8 時(shí)將 JRockit 虛擬機(jī)和 HotSpot 虛擬機(jī)進(jìn)行了整合。
PS:JEP 是 JDK Enhancement Proposal 的縮寫(xiě),翻譯成中文是 JDK 改進(jìn)提案。你也可以把它理解為 JDK 的更新文檔。
通過(guò)官方的描述,我們似乎找到了答案,也就是說(shuō),之所以要取消“永久代”是因?yàn)?Java 官方收購(gòu)了 JRockit,之后在將 JRockit 和 HotSpot 進(jìn)行整合時(shí),因?yàn)?JRockit 中沒(méi)有“永久代”,所以把永久代給移除了。
PS:上面的那段描述好像說(shuō)的已經(jīng)很清楚了,但又好像什么也沒(méi)說(shuō)。這就好比,我問(wèn)你“為什么要買(mǎi)車(chē)?”,你說(shuō)“別人都買(mǎi)車(chē)了,所以我要買(mǎi)車(chē)”,但為什么別人要買(mǎi)車(chē)?
2.背后的原因
上述給出了移除永久代的回答,但卻沒(méi)有給出背后的原因,那接下來(lái)我們就來(lái)討論一下,為什么要移除永久代?以及為什么要有元空間?
2.1 降低 OOM
當(dāng)使用永久代實(shí)現(xiàn)方法區(qū)時(shí),永久代的最大容量受制于 PermSize 和 MaxPermSize 參數(shù)設(shè)置的大小,而這兩個(gè)參數(shù)的大小又很難確定,因?yàn)樵诔绦蜻\(yùn)行時(shí)需要加載多少類(lèi)是很難估算的,如果這兩個(gè)參數(shù)設(shè)置的過(guò)小就會(huì)頻繁的觸發(fā) FullGC 和導(dǎo)致 OOM(Out of Memory,內(nèi)存溢出)。
但是,當(dāng)使用元空間替代了永久代之后,出現(xiàn) OOM 的幾率就被大大降低了,因?yàn)樵臻g使用的是本地內(nèi)存,這樣元空間的大小就只和本地內(nèi)存的大小有關(guān)了,從而大大降低了 OOM 的問(wèn)題。
2.2 降低運(yùn)維成本
因?yàn)樵臻g使用的是本地內(nèi)存,這樣就無(wú)需運(yùn)維人員再去專(zhuān)門(mén)設(shè)置和調(diào)整元空間的大小了。
3.方法區(qū)發(fā)展史
在 HotSpot 虛擬機(jī)中,方法區(qū)的實(shí)現(xiàn)經(jīng)歷了以下 3 個(gè)階段:
- JDK 1.6 及之前:方法區(qū)使用永久代實(shí)現(xiàn),靜態(tài)變量存放在永久代;
- JDK 1.7 :“去永久代”的前置版本,還存在永久代,不過(guò)已經(jīng)將字符串常量池和靜態(tài)變量從永久代移到了堆上;
- JDK 1.8 及以后:無(wú)永久代,使用元空間(存放在本地內(nèi)存中)實(shí)現(xiàn)方法區(qū),常量保存在元空間,但字符串常量池和靜態(tài)變量依然保存在堆中。
總結(jié)
永久代和元空間都是 HotSpot 虛擬機(jī)對(duì)《Java 虛擬機(jī)規(guī)范》中方法區(qū)的實(shí)現(xiàn),在 JDK 1.8 之前 HotSpot 是使用永久代來(lái)實(shí)現(xiàn)方法區(qū)的,但這樣會(huì)導(dǎo)致 JVM 調(diào)優(yōu)比較困難,且容易發(fā)生 OOM 的問(wèn)題,而 JDK 1.8 及之后,使用的是元空間存放在本地內(nèi)存中的方式來(lái)替代永久代的,這樣就降低了 OOM 發(fā)生的可能性,也是 JRockit 和 HotSpot 融合之后的改動(dòng)之一。
參考 & 鳴謝
openjdk.org/jeps/122