對方法區(qū)和永久代的理解

目前有三大Java虛擬機(jī):HotSpot,oracle JRockit,IBM J9。

JRockit是oracle發(fā)明的,用于其WebLogic服務(wù)器,IBM JVM是IBM發(fā)明的用于其Websphere服務(wù)器(所以在某行開發(fā)的時(shí)候,他們用的是IBM的JDK,因?yàn)樗麄兪褂玫腎BM的應(yīng)用程序服務(wù)器Websphere,使用其他JDK可能存在兼容性問題)。

JRockit和J9不存在永久代這種說法。這里只討論HotSpot虛擬機(jī),這也是目前使用的最多的JVM。Sun JDK7 HotSpot虛擬機(jī)的內(nèi)存模型如下圖所示:

image

1、什么是方法區(qū)

一、方法區(qū)與永久代
這兩個(gè)是非常容易混淆的概念,永久代的對象放在方法區(qū)中,就會想當(dāng)然地認(rèn)為,方法區(qū)就等同于持久代的內(nèi)存區(qū)域。事實(shí)上兩者是這樣的關(guān)系:

《Java虛擬機(jī)規(guī)范》只是規(guī)定了有方法區(qū)這么個(gè)概念和它的作用,并沒有規(guī)定如何去實(shí)現(xiàn)它。那么,在不同的 JVM 上方法區(qū)的實(shí)現(xiàn)肯定是不同的了。 同時(shí)大多數(shù)用的JVM都是Sun公司的HotSpot。在HotSpot上把GC分代收集擴(kuò)展至方法區(qū),或者說使用永久代來實(shí)現(xiàn)方法區(qū)。換句話說:方法區(qū)是一種規(guī)范,永久代是Hotspot針對這一規(guī)范的一種實(shí)現(xiàn)。而永久代本身也在迭代中:

在Java 6中,方法區(qū)中包含的數(shù)據(jù),除了JIT編譯生成的代碼存放在native memory的CodeCache區(qū)域,其他都存放在永久代;
在Java 7中,Symbol的存儲從PermGen移動到了native memory,并且把靜態(tài)變量從instanceKlass末尾(位于PermGen內(nèi))移動到了java.lang.Class對象的末尾(位于普通Java heap內(nèi));
在Java 8中,永久代被徹底移除,取而代之的是另一塊與堆不相連的本地內(nèi)存——元空間(Metaspace),?XX:MaxPermSize 參數(shù)失去了意義,取而代之的是-XX:MaxMetaspaceSize。

對于Java8, HotSpots取消了永久代,那么是不是也就沒有方法區(qū)了呢?當(dāng)然不是,方法區(qū)是一個(gè)規(guī)范,規(guī)范沒變,它就一直在。那么取代永久代的就是元空間。它與永久代有什么不同的?

存儲位置不同,永久代是堆的一部分,和新生代,老年代地址是連續(xù)的,而元空間屬于本地內(nèi)存;

存儲內(nèi)容不同,元空間存儲類的元信息,靜態(tài)變量和常量池等并入堆中。相當(dāng)于永久代的數(shù)據(jù)被分到了堆和元空間中。

二、方法區(qū)里存著什么?
既然永久代是方法區(qū)的一種實(shí)現(xiàn),那么在Hotspot下,方法區(qū)就等于永久代,也被稱為非堆。那方法區(qū)里都存著什么呢?先拋結(jié)論:

靜態(tài)變量 + 常量 + 類信息(構(gòu)造方法/接口定義) + 運(yùn)行時(shí)常量池存在方法區(qū)中 。

2、方法區(qū)和永久代的關(guān)系

在Java虛擬機(jī)規(guī)范中,方法區(qū)在虛擬機(jī)啟動的時(shí)候創(chuàng)建,雖然方法區(qū)是堆的邏輯組成部分,但是簡單的虛擬機(jī)實(shí)現(xiàn)可以選擇不在方法區(qū)實(shí)現(xiàn)垃圾回收與壓縮。這個(gè)版本的虛擬機(jī)規(guī)范也不限定實(shí)現(xiàn)方法區(qū)的內(nèi)存位置和編譯代碼的管理策略。所以不同的JVM廠商,針對自己的JVM可能有不同的方法區(qū)實(shí)現(xiàn)方式。

永久代是Hotspot虛擬機(jī)特有的概念,是方法區(qū)的一種實(shí)現(xiàn),別的JVM都沒有這個(gè)東西。在Java 8中,永久代被徹底移除,取而代之的是另一塊與堆不相連的本地內(nèi)存——元空間。
永久代或者“Perm Gen”包含了JVM需要的應(yīng)用元數(shù)據(jù),這些元數(shù)據(jù)描述了在應(yīng)用里使用的類和方法。注意,永久代不是Java堆內(nèi)存的一部分。永久代存放JVM運(yùn)行時(shí)使用的類。永久代同樣包含了Java SE庫的類和方法。永久代的對象在full GC時(shí)進(jìn)行垃圾收集。

在HotSpot中,設(shè)計(jì)者將方法區(qū)納入GC分代收集。HotSpot虛擬機(jī)堆內(nèi)存被分為新生代和老年代,對堆內(nèi)存進(jìn)行分代管理。

方法區(qū)和永久代的關(guān)系很像Java中接口和類的關(guān)系,類實(shí)現(xiàn)了接口,而永久代就是HotSpot虛擬機(jī)對虛擬機(jī)規(guī)范中方法區(qū)的一種實(shí)現(xiàn)方式。

我們知道在HotSpot虛擬機(jī)中存在三種垃圾回收現(xiàn)象,minor GC、major GC和full GC。對新生代進(jìn)行垃圾回收叫做minor GC,對老年代進(jìn)行垃圾回收叫做major GC,同時(shí)對新生代、老年代和永久代進(jìn)行垃圾回收叫做full GC。許多major GC是由minor GC觸發(fā)的,所以很難將這兩種垃圾回收區(qū)分開。major GC和full GC通常是等價(jià)的,收集整個(gè)GC堆。但因?yàn)镠otSpot VM發(fā)展了這么多年,外界對各種名詞的解讀已經(jīng)完全混亂了,當(dāng)有人說“major GC”的時(shí)候一定要問清楚他想要指的是上面的full GC還是major GC。

3、元空間

上面說過,HotSpot虛擬機(jī)在1.8之后已經(jīng)取消了永久代,改為元空間,類的元信息被存儲在元空間中。元空間沒有使用堆內(nèi)存,而是與堆不相連的本地內(nèi)存區(qū)域。所以,理論上系統(tǒng)可以使用的內(nèi)存有多大,元空間就有多大,所以不會出現(xiàn)永久代存在時(shí)的內(nèi)存溢出問題。這項(xiàng)改造也是有必要的,永久代的調(diào)優(yōu)是很困難的,雖然可以設(shè)置永久代的大小,但是很難確定一個(gè)合適的大小,因?yàn)槠渲械挠绊懸蛩睾芏啵热珙悢?shù)量的多少、常量數(shù)量的多少等。永久代中的元數(shù)據(jù)的位置也會隨著一次full GC發(fā)生移動,比較消耗虛擬機(jī)性能。同時(shí),HotSpot虛擬機(jī)的每種類型的垃圾回收器都需要特殊處理永久代中的元數(shù)據(jù)。將元數(shù)據(jù)從永久代剝離出來,不僅實(shí)現(xiàn)了對元空間的無縫管理,還可以簡化Full GC以及對以后的并發(fā)隔離類元數(shù)據(jù)等方面進(jìn)行優(yōu)化。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容