Java 內存分區之 堆外內存 Metaspace 元空間 取永久代PermGen 而代之

在稍微瞭解Java內存分區的時候,大多數文章都是出自深入理解jvm這本書,上來就是給你分了 程序計數器,Java虛擬機棧,本地方法棧,堆,方法區,還有個直接內存,還說方法區裏面有個常量池。在寫這本書的時候,jdk還在1.6,但是現在2020年jdk都已經jdk14了,雖然還沒普及jdk14,但是估計以後都會使用的吧,就像現在基本最低都要使用jdk1.8一樣。1.7是在2011年發佈的,1.8是14年發佈的,時至今日,內存分區已經有了些許變化。雖然理論還是大差不差的。

永久代主要存放以下數據:

  • JVM internal representation of classes and their metadata //類及其元數據的JVM內部表示
  • Class statics //類的靜態
  • Interned strings //實際字符串,說的就是常量池吧

從 JDK7 開始,JDK 開發者們就有消滅永久代的打算了。有部分數據移到永久代之外了:

  • Symbols => native memory // 符號引用 >本機內存
  • Interned strings => Java Heap // Interned string => Java堆
  • Class statics => Java Heap //類statics => Java堆

到了 JDK8,這個工作終於完成了,徹底廢棄了 PermGen,Metaspace 取而代之。

方法區都存了些什麼

  • JVM中類的元數據在Java堆中的存儲區域。
  • Java類對應的HotSpot虛擬機中的內部表示也存儲在這裏。
  • 類的層級信息,字段,名字。
  • 方法的編譯信息及字節碼。
  • 變量
  • 常量池和符號解析

持久代的大小

  • 它的上限是MaxPermSize,默認是64M
  • Java堆中的連續區域 : 如果存儲在非連續的堆空間中的話,要定位出持久代到新對象的引用非常複雜並且耗時。卡表(card table),是一種記憶集(Remembered Set),它用來記錄某個內存代中普通對象指針(oops)的修改。
  • 持久代用完後,會拋出OutOfMemoryError "PermGen space"異常。解決方案:應用程序清理引用來觸發類卸載;增加MaxPermSize的大小。
  • 需要多大的持久代空間取決於類的數量,方法的大小,以及常量池的大小。

爲什麼移除持久代

  • 它的大小是在啓動時固定好的——很難進行調優。-XX:MaxPermSize,設置成多少好呢?
  • HotSpot的內部類型也是Java對象:它可能會在Full GC中被移動,同時它對應用不透明,且是非強類型的,難以跟蹤調試,還需要存儲元數據的元數據信息(meta-metadata)。
  • 簡化Full GC:每一個回收器有專門的元數據迭代器。
  • 可以在GC不進行暫停的情況下併發地釋放類數據。
  • 使得原來受限於持久代的一些改進未來有可能實現

根據上面的各種原因,永久代最終被移除,方法區移至Metaspace,字符串常量移至Java Heap(待測試確認)

什麼是 Metaspace

Metaspace 區域位於堆外,所以它的最大內存大小取決於系統內存,而不是堆大小,我們可以指定 MaxMetaspaceSize 參數來限定它的最大內存。
Metaspace 是用來存放 class metadata 的,class metadata 用於記錄一個 Java 類在 JVM 中的信息,包括但不限於 JVM class file format 的運行時數據: 
1、Klass 結構,這個非常重要,把它理解爲一個 Java 類在虛擬機內部的表示吧;
2、method metadata,包括方法的字節碼、局部變量表、異常表、參數信息等;
3、常量池;
4、註解;
5、方法計數器,記錄方法被執行的次數,用來輔助 JIT 決策
6、 其他
雖然每個 Java 類都關聯了一個 java.lang.Class 的實例,而且它是一個貯存在堆中的 Java 對象。但是類的 class metadata 不是一個 Java 對象,它不在堆中,而是在 Metaspace 中。

什麼時候分配 Metaspace 空間

當一個類被加載時,它的類加載器會負責在 Metaspace 中分配空間用於存放這個類的元數據

什麼時候回收 Metaspace 空間

分配給一個類的空間,是歸屬於這個類的類加載器的,只有當這個類加載器卸載的時候,這個空間纔會被釋放。
所以,只有當這個類加載器加載的所有類都沒有存活的對象,並且沒有到達這些類和類加載器的引用時,相應的 Metaspace 空間纔會被 GC 釋放。

配置 Metaspace 空間

  • -XX:MaxMetaspaceSize:Metaspace 總空間的最大允許使用內存,默認是不限制。

  • -XX:CompressedClassSpaceSize:Metaspace 中的 Compressed Class Space 的最大允許內存,默認值是 1G,這部分會在 JVM 啓動的時候向操作系統申請 1G 的虛擬地址映射,但不是真的就用了操作系統的 1G 內存。

Metaspace 和 GC

Metaspace 只在 GC 運行並且卸載類加載器的時候纔會釋放空間。當然,在某些時候,需要主動觸發 GC 來回收一些沒用的 class metadata,即使這個時候對於堆空間來說,還達不到 GC 的條件。
Metaspace 可能在兩種情況下觸發 GC:
1、分配空間時:虛擬機維護了一個閾值,如果 Metaspace 的空間大小超過了這個閾值,那麼在新的空間分配申請時,虛擬機首先會通過收集可以卸載的類加載器來達到複用空間的目的,而不是擴大 Metaspace 的空間,這個時候會觸發 GC。這個閾值會上下調整,和 Metaspace 已經佔用的操作系統內存保持一個距離。
2、碰到 Metaspace OOM:Metaspace 的總使用空間達到了 MaxMetaspaceSize 設置的閾值,或者 Compressed Class Space 被使用光了,如果這次 GC 真的通過卸載類加載器騰出了很多的空間,這很好,否則的話,我們會進入一個糟糕的 GC 週期,即使我們有足夠的堆內存。

各個jdk的發行時間

從這個表中我們可以看出一個非常有意思的現象,就是JDK的每一個版本號都使用一個開發代號表示(就是表中的中文名)。而且從JDK1.2.2 開始,主要版本(如1.3,1.4,5.0)都是以鳥類或哺乳動物來命名的. 而它們的bug修正版本(如1.2.2,1.3.1,1.4.2)都是以昆蟲命名的。

時間-事件軸

1995年5月23日,Java語言誕生
1996年1月,第一個JDK-JDK1.0誕生
1996年4月,10個最主要的操作系統供應商申明將在其產品中嵌入JAVA技術
1996年9月,約8.3萬個網頁應用了JAVA技術來製作
1997年2月18日,JDK1.1發佈
1997年4月2日,JavaOne會議召開,參與者逾一萬人,創當時全球同類會議規模之紀錄
1997年9月,JavaDeveloperConnection社區成員超過十萬
1998年2月,JDK1.1被下載超過2,000,000次
1998年12月8日,JAVA2企業平臺J2EE發佈
1999年6月,SUN公司發佈Java的三個版本:標準版、企業版和微型版(J2SE、J2EE、J2ME)
2000年5月8日,JDK1.3發佈
2000年5月29日,JDK1.4發佈
2001年6月5日,NOKIA宣佈,到2003年將出售1億部支持Java的手機
2001年9月24日,J2EE1.3發佈
2002年2月13日,J2SE1.4發佈,自此Java的計算能力有了大幅提升。
2004年9月30日18:00PM,J2SE1.5發佈,是Java語言的發展史上的又一里程碑事件。爲了表示這個版本的重要性,J2SE1.5更名爲J2SE5.0
2005年6月,JavaOne大會召開,SUN公司公開Java SE 6。此時,Java的各種版本已經更名以取消其中的數字“2”:J2EE更名爲Java EE, J2SE更名爲Java SE,J2ME更名爲Java ME。
2006年11月13日,SUN公司宣佈Java全線採納GNU General Public License Version 2,從而公開了Java的源代碼。

附2張自己造的圖

jdk1.7的時候,一張jvm內存的gc圖

這個時候PermGen還是存在的,驗證了文章開頭的理論。

jdk1.8的時候,一張jvm內存的gc圖

配置的jvm的參數;

-XX:+PrintGCDetails -Xms200M -Xmx200M -Xmn10M -XX:SurvivorRatio=8 -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:CompressedClassSpaceSize=5M

看到方法區真的就不在了,被Metaspace區替代了。也驗證文章開頭的理論。

 

參考:

深入理解堆外內存 Metaspace

Metaspace 之一:Metaspace整體介紹(永久代被替換原因、元空間特點、元空間內存查看分析方法)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章