您好,登錄后才能下訂單哦!
這篇文章主要介紹了JVM中永久代和元空間是什么意思,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
Java 的內存中有一塊稱之為方法區的部分,在 JDK8 之前, Hotspot 虛擬機中的實現方式為永久代(Permanent Generation),別的JVM都沒有這個東西。
在過去(當自定義類加載器使用不普遍的時候),類幾乎是“靜態的”并且很少被卸載和回收,因此類也可以被看成“永久的”。另外由于類作為 JVM 實現的一部分,它們不由程序來創建,因為它們也被認為是“非堆”的內存。
永久代是一段連續的內存空間,我們在 JVM 啟動之前可以通過設置-XX:MaxPermSize
的值來控制永久代的大小,32 位機器默認的永久代的大小為 64M,64 位的機器則為 85M。
永久代的垃圾回收和老年代的垃圾回收是綁定的,一旦其中一個區域被占滿,這兩個區都要進行垃圾回收。但是有一個明顯的問題,由于我們可以通過?XX:MaxPermSize
設置永久代的大小,一旦類的元數據超過了設定的大小,程序就會耗盡內存,并出現內存溢出錯誤 (java.lang.OutOfMemoryError: PermGen space)。
為什么類的元數據占用內存會那么大?因為在 JDK7 之前的 HotSpot 虛擬機中,納入字符串常量池的字符串被存儲在永久代中,因此導致了一系列的性能問題和內存溢出錯誤。
為了解決這些性能問題,也為了能夠讓 Hotspot 能和其他的虛擬機一樣管理,元空間
就產生了。
元空間是 Hotspot 在 JDK8 中新加的內容,其本質和永久代類似,都是對 JVM 規范中方法區的實現。不過元空間與永久代之間最大的區別在于:
元空間并不在虛擬機中,而是使用
本地內存
。因此,默認情況下,元空間的大小僅受本地內存限制,但可以通過以下參數來指定元空間的大小:-XX:MetaspaceSize
初始空間大小,達到該值就會觸發垃圾收集進行類型卸載,同時GC會對該值進行調整:如果釋放了大量的空間,就適當降低該值;如果釋放了很少的空間,那么在不超過MaxMetaspaceSize時,適當提高該值。 -XX:MaxMetaspaceSize 最大空間,默認是沒有限制的。
除了上面兩個指定大小的選項以外,還有兩個與 GC 相關的屬性:
-XX:MinMetaspaceFreeRatio
在GC之后,最小的Metaspace剩余空間容量的百分比,減少為分配空間所導致的垃圾收集
-XX:MaxMetaspaceFreeRatio
在GC之后,最大的Metaspace剩余空間容量的百分比,減少為釋放空間所導致的垃圾收集
由于類的元數據分配在本地內存中,元空間的最大可分配空間就是系統可用內存空間。因此,我們就不會遇到永久代存在時的內存溢出錯誤,也不會出現泄漏的數據移到交換區這樣的事情。最終用戶可以為元空間設置一個可用空間最大值,如果不進行設置,JVM 會自動根據類的元數據大小動態增加元空間的容量。
注意:永久代的移除并不代表自定義的類加載器泄露問題就解決了。因此,你還必須監控你的內存消耗情況,因為一旦發生泄漏,會占用你的大量本地內存,并且還可能導致交換區交換更加糟糕。
元空間的內存管理由元空間虛擬機來完成。
先前,對于類的元數據我們需要不同的垃圾回收器進行處理,現在只需要執行元空間虛擬機的 C++ 代碼即可完成。在元空間中,類和其元數據的生命周期和其對應的類加載器是相同的。話句話說,只要類加載器存活,其加載的類的元數據也是存活的,因而不會被回收掉。
準確的來說,每一個類加載器的存儲區域都稱作一個元空間,所有的元空間合在一起就是我們一直說的元空間。當一個類加載器被垃圾回收器標記為不再存活,其對應的元空間會被回收。在元空間的回收過程中沒有重定位和壓縮等操作。但是元空間內的元數據會進行掃描來確定 Java 引用。
那具體是如何管理的呢?
元空間虛擬機負責元空間的分配,其采用的形式為組塊分配。組塊的大小因類加載器的類型而異。在元空間虛擬機中存在一個全局的空閑組塊列表。
當一個類加載器需要組塊時,它就會從這個全局的組塊列表中獲取并維持一個自己的組塊列表。
當一個類加載器不再存活時,那么其持有的組塊將會被釋放,并返回給全局組塊列表。
類加載器持有的組塊又會被分成多個塊,每一個塊存儲一個單元的元信息。組塊中的塊是線性分配(指針碰撞分配形式)。組塊分配自內存映射區域。這些全局的虛擬內存映射區域以鏈表形式連接,一旦某個虛擬內存映射區域清空,這部分內存就會返回給操作系統。
運行時常量池在 JDK6 及之前版本的 JVM 中是方法區的一部分,而在 HotSpot 虛擬機中方法區的實現是永久代(Permanent Generation)。所以運行時常量池也是在永久代的。
但是 JDK7 及之后版本的 JVM 已經將字符串常量池從方法區中移了出來,在堆(Heap)中開辟了一塊區域存放運行時常量池。
String.intern()
是一個 Native 方法,它的作用是:如果運行時常量池中已經包含一個等于此 String 對象內容的字符串,則返回常量池中該字符串的引用;如果沒有,則在常量池中創建與此 String 內容相同的字符串,并返回常量池中創建的字符串的引用。
前面已經提到,元空間虛擬機采用了組塊分配的形式,同時區塊的大小由類加載器類型決定。類信息并不是固定大小,因此有可能分配的空閑區塊和類需要的區塊大小不同,這種情況下可能導致碎片存在。元空間虛擬機目前并不支持壓縮操作,所以碎片化是目前最大的問題。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“JVM中永久代和元空間是什么意思”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。