自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

探索 JVM 的隱秘角落:元空間詳解

開發(fā)
本文從JVM發(fā)展歷程和底層結(jié)構(gòu)并結(jié)合一個(gè)實(shí)際案例帶讀者深入了解JVM元空間核心知識(shí)點(diǎn),希望對(duì)你有所啟發(fā)。

隨著Java應(yīng)用程序日益復(fù)雜和龐大,JVM(Java虛擬機(jī))的性能優(yōu)化變得尤為重要。在JVM的各種組件中,元空間(Metaspace)作為類元數(shù)據(jù)的存儲(chǔ)區(qū)域,扮演著關(guān)鍵角色。本文將深入探討JVM元空間的工作原理、架構(gòu)設(shè)計(jì)及其對(duì)應(yīng)用性能的影響,并提供實(shí)際的調(diào)優(yōu)建議。通過本文,讀者不僅能夠全面了解元空間的基本概念,還能掌握如何有效管理和優(yōu)化這一重要資源。

什么是JVM方法區(qū)

方法區(qū)主要是用于存儲(chǔ)類信息、靜態(tài)變量以及常量信息的。是各個(gè)線程共享的一個(gè)區(qū)域。我們都知道JVM中有個(gè)區(qū)域叫堆區(qū),所以有時(shí)候人們也會(huì)稱方法區(qū)為Non-Heap(非堆)。

在JDK8之前方法區(qū)存放在一個(gè)叫永久代的空間里。 在JDK8之后由于HotSpot 和JRockit 的合并,所以方法區(qū)就被作為元數(shù)據(jù)區(qū)了。

方法區(qū)和永久代是什么關(guān)系?

其實(shí)方法區(qū)并不是一個(gè)實(shí)際的區(qū)域,他不過是JVM虛擬機(jī)規(guī)范提出的一個(gè)概念而已。在HotSpot 實(shí)現(xiàn)方法區(qū)的方式就在JVM內(nèi)存中劃分一個(gè)區(qū)域作為永久代來存放這些數(shù)據(jù)。

在JDK8之前我們可以用下面的參數(shù)來調(diào)整永久代的大小

-XX:PermSize=N //方法區(qū) (永久代) 初始大小
-XX:MaxPermSize=N //方法區(qū) (永久代) 最大大小,超過這個(gè)值將會(huì)拋出 OutOfMemoryError 異常:java.lang.OutOfMemoryError: PermGen

為什么JDK8之后要把永久代 (PermGen)換成元數(shù)據(jù)區(qū)(MetaSpace)

將數(shù)據(jù)放在永久代固然沒問題,但是隨著時(shí)間的推移,方法區(qū)使用的空間可能會(huì)逐漸變大,若我們分配大小不當(dāng)很可能造成線上OOM問題,所以設(shè)計(jì)者們就在方法區(qū)移動(dòng)到本地內(nèi)存中,通過本地內(nèi)存來存放數(shù)據(jù)。并且元數(shù)據(jù)區(qū)默認(rèn)分配值為unlimited(我們也可以通過-XX:MetaspaceSize來動(dòng)態(tài)調(diào)整),理論上是沒有明確大小,是可以動(dòng)態(tài)分配空間的,這樣一來由于元數(shù)據(jù)區(qū)就不會(huì)受到JVM內(nèi)存分配的約束了,所以理論上發(fā)生OOM的概率會(huì)小于永久代。

深入理解Java虛擬機(jī)關(guān)于方法區(qū)的說法

筆者查閱權(quán)威《深入理解Java虛擬機(jī)》 中看到,《Java虛擬機(jī)規(guī)范》 對(duì)于方法區(qū)的實(shí)現(xiàn)即元空間或者永久代垃圾回收行為沒有強(qiáng)制要求。 原因很簡(jiǎn)單,方法區(qū)進(jìn)行垃圾收集的回收的收益不是很大,它并不像堆內(nèi)存的新生代那樣,在一次新生代的垃圾回收就能回收70%-90% 的內(nèi)存空間。這也使得大部分人(包括筆者)認(rèn)為方法區(qū)不涉及GC的,實(shí)際上對(duì)于jdk8 版本的Hotspot虛擬機(jī)而言,JVM 中某一個(gè)類符合以下這3個(gè)條件時(shí)將會(huì)卸載類并回收這個(gè)類的元數(shù)據(jù)空間:

  • 在堆中沒有任何基于當(dāng)前類或者基于該類派生子類的實(shí)例。
  • 該類的java.lang.Class對(duì)象沒有在任何地方被引用,以及無法通過反射等方式訪問該類的方法。
  • 加載該類的類加載器被回收,這個(gè)條件除非是精心設(shè)計(jì)過的可替換類加載器的場(chǎng)景,否者很難實(shí)現(xiàn)。

需要注意的是,在判斷是否有實(shí)例還在使用當(dāng)前類以及是否有類加載器引用這個(gè)類這兩個(gè)步驟的時(shí)候,為了能夠明確這兩點(diǎn),可能需要掃描全部堆空間的,這也就意味著元空間的回收可能伴隨著FullGC。

代理對(duì)象創(chuàng)建不當(dāng)導(dǎo)致元空間OOM問題

可以看到最后一點(diǎn)比較苛刻,所以就導(dǎo)致如果我們使用Spring等框架通過增強(qiáng)技術(shù)生成大量的新類型載入元空間內(nèi)存,導(dǎo)致元空間內(nèi)存溢出(Caused by: java.lang.OutOfMemoryError: Metaspace) ,就像下面這段代碼一樣,為了更快看到效果,我們手動(dòng)設(shè)置一下元空間大小-XX:MetaspaceSize=100m -XX:MaxMetaspaceSize=100m:

public static void main(String[] args) {
       while (true){
           Enhancer enhancer = new Enhancer();
           //設(shè)置代理目標(biāo)
           enhancer.setSuperclass(EmptyObject.class);

           enhancer.setUseCache(false);

           //設(shè)置單一回調(diào)對(duì)象,在調(diào)用中攔截對(duì)目標(biāo)方法的調(diào)用
           enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invokeSuper(objects, args));

           enhancer.create();
       }

    }

我們通過jconsole定位查看當(dāng)前進(jìn)程的類加載信息:

可以看到大量EmptyObject的增強(qiáng)類被加載至元空間中:

鍵入命令jmap 定位加載的類信息再次進(jìn)行確認(rèn):

jmap -histo 4532

可以看到生成了大量的net.sf.cglib.proxy相關(guān)的類

 num     #instances         #bytes  class name
----------------------------------------------
   1:       3824742      600680704  [C
   2:       1932145      170028760  java.lang.reflect.Method
   3:       3806008       91344192  java.lang.String
   4:       1779516       37754664  [Ljava.lang.Class;
   5:         26568       15064520  [I
   6:        618402       14841648  net.sf.cglib.core.Signature
   7:         79344       12595728  java.lang.Class
   8:        154765       12381200  java.lang.reflect.Constructor
   9:        308844        9883008  net.sf.cglib.proxy.MethodProxy
  10:        308844        9883008  net.sf.cglib.proxy.MethodProxy$CreateInfo

我們以MethodProxy進(jìn)行定位可以看到這個(gè)類是在create方法創(chuàng)建的,這也就意味著上述代碼的最后一個(gè)create方法會(huì)創(chuàng)建大量的MethodProxy并存到元空間中導(dǎo)致元空間內(nèi)存溢出:

public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
        MethodProxy proxy = new MethodProxy();
        proxy.sig1 = new Signature(name1, desc);
        proxy.sig2 = new Signature(name2, desc);
        proxy.createInfo = new CreateInfo(c1, c2);
        return proxy;
    }

所以盡管說jdk8將類信息存到原空間中,但我們?nèi)粘_M(jìn)行開發(fā)也需要留意對(duì)于cglib等增強(qiáng)技術(shù)的使用是否得當(dāng),如果發(fā)現(xiàn)大量的增強(qiáng)類出現(xiàn)在元空間時(shí),需要及時(shí)定位并解決。

責(zé)任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關(guān)推薦

2020-07-08 11:20:00

戴爾

2020-08-14 10:56:17

云安全云計(jì)算網(wǎng)絡(luò)安全

2020-12-20 18:02:15

暗網(wǎng)監(jiān)控暗網(wǎng)漏洞

2020-12-15 10:52:44

CIO企業(yè)網(wǎng)

2020-07-13 09:35:40

5G網(wǎng)絡(luò)技術(shù)

2020-07-06 09:07:30

爬蟲彈幕發(fā)射器

2022-06-22 15:18:29

開發(fā)

2019-10-23 08:16:50

Java8JVM內(nèi)存

2022-05-24 09:50:27

元宇宙藝術(shù)人工智能

2023-06-08 08:04:37

Java8元空間永久代

2023-10-20 09:42:43

人工智能元宇宙

2022-03-03 12:24:17

暗碼追蹤數(shù)字水印打印機(jī)

2024-04-09 08:41:41

JVM類加載Java

2021-08-23 10:14:20

鴻蒙HarmonyOS應(yīng)用

2010-12-10 15:40:58

JVM內(nèi)存管理

2022-01-17 22:09:50

JVM方法區(qū)數(shù)據(jù)

2022-05-07 19:15:35

微軟元宇宙雪崩

2019-05-06 08:37:14

JVMLinux內(nèi)存

2010-09-25 15:19:01

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)