1、java內(nèi)存管理-方法區(qū)(元空間的簡(jiǎn)介)

我這里使用的版本為:java version "1.8.0_31",64位的機(jī)器


首先,這里會(huì)先用一個(gè)工具查看內(nèi)存的信息:jconsole相關(guān)的介紹:https://docs.oracle.com/javase/1.5.0/docs/guide/management/jconsole.html


裝了jdk,并且配置了環(huán)境變量,可以直接在控制臺(tái)中輸入jconsole,就會(huì)彈出對(duì)應(yīng)的界面。

這里我首先寫一段代碼,僅僅是sleep一段時(shí)間,這里就可以使用jconsole來(lái)查看這一段運(yùn)行著

的代碼。

package?cn.yishijie.jvm;import?java.util.concurrent.TimeUnit;public?class?JDK8Memory?{

????public?static?void?main(String[]?args)?throws?InterruptedException{

????????//?睡一個(gè)小時(shí)
????????TimeUnit.HOURS.sleep(1L);
????}
}

運(yùn)行起來(lái),然后直接在控制臺(tái)輸入jconsole:彈出以下界面


點(diǎn)擊箭頭的哪個(gè)進(jìn)程,然后選擇不安全連接然后就可以進(jìn)去查看這個(gè)類運(yùn)行的具體情況了。

直接進(jìn)入到內(nèi)存的那個(gè)菜單進(jìn)入:



點(diǎn)開(kāi)A區(qū)域可以看到內(nèi)存的劃分情況;點(diǎn)B可以執(zhí)行一次GC;C可以查看對(duì)應(yīng)的內(nèi)存的使用情況;

D可以圖示的看到內(nèi)存情況,鼠標(biāo)移動(dòng)到該區(qū)域停住可以彈出該區(qū)域的名稱,點(diǎn)擊可以選中對(duì)應(yīng)的區(qū)域

并展示該區(qū)域的信息。

這里分為:堆和非堆。

堆:?
??PS?Old?Gen:?老年代
??PS?Eden?Space:?新生代eden區(qū)
??PS?Survivor?Space:?新生代幸存區(qū)
??
非堆:
??Meta?Data:?元空間
??Code?Cache:?代碼緩存
??Compressed?Class?Space:?壓縮類空間

啟動(dòng)的時(shí)候,增加VM參數(shù):-XX:+PrintCommandLineFlags

然后可以看到:

-XX:InitialHeapSize=133094272?-XX:MaxHeapSize=2129508352?-XX:+PrintCommandLineFlags?-XX:+UseCompressedClassPointers?-XX:+UseCompressedOops?-XX:-UseLargePagesIndividualAllocation?-XX:+UseParallelGC

可以發(fā)現(xiàn)這里使用的垃圾回收器: ParallelGC ,它會(huì)和Parallel Old搭配使用。其實(shí)上面的PS就是表示這個(gè)垃圾收集器組合。



1、方法區(qū)

方法區(qū)是線程共享的內(nèi)存區(qū)域,用于存儲(chǔ)jvm加載的

類型信息,用本地內(nèi)存元空間(meta space)實(shí)現(xiàn)。

垃圾回收會(huì)被該內(nèi)存進(jìn)行處理,主要是對(duì)類型的卸載和

常量池的回收。

方法區(qū)在JVM啟動(dòng)的時(shí)候被創(chuàng)建,它的大小決定了能保存

的類的數(shù)量,如果超出,會(huì)出現(xiàn)oom的異常。


方式區(qū)使用上述的Meta Data來(lái)實(shí)現(xiàn),使用的不是jvm的內(nèi)存,而是物理機(jī)器的內(nèi)存,

所以它的上限是物理機(jī)器的內(nèi)存。

啟動(dòng)的時(shí)候加入虛擬機(jī)參數(shù):-XX:+PrintFlagsFinal,在一堆輸出中找到下面元空間的初始值和最大值

MetaspaceSize = 21807104 = 20.8M

MaxMetaspaceSize = 4294901760 = 4G

當(dāng)元空間的大小使用量到達(dá)初始值大小就會(huì)觸發(fā)一次full gc,如果還不夠的話,就會(huì)擴(kuò)展大小,但是最大不能

超過(guò)MaxMetaspaceSize的大小。

這里驗(yàn)證下full gc的產(chǎn)生,回看到原來(lái)的那個(gè)圖,然后選中Meta Data


可以看到,我這個(gè)程序運(yùn)行起來(lái),Meta Data基本就8M多,所以我們可以通過(guò):

-XX:MetaspaceSize=6M??6M內(nèi)存
-XX:MaxMetaspaceSize=200M??200M內(nèi)存
-XX:+PrintGCDetails??打印gc詳情

來(lái)設(shè)置大小,從而觸發(fā)full gc


這里設(shè)置了元空間的初始大小為6M,同時(shí)也發(fā)生了元空間也發(fā)生了Full GC。這里是需要為什么要jconsole連接上才會(huì)Full Gc。

后來(lái)我試了設(shè)置成4M,發(fā)現(xiàn)確實(shí)可以一下就打印出來(lái)??磥?lái)應(yīng)該是程序本身加載的類并沒(méi)有達(dá)到6M,應(yīng)該是連接jconsole的時(shí)候,還會(huì)加載類,占用了一部分空間,達(dá)到8M就觸發(fā)了。


這個(gè)我也做個(gè)實(shí)驗(yàn)了(vm參數(shù)中加入:-verbose:class即可),確實(shí)是這樣子。這里我只是截取出打印的一部分記錄:

[Loaded?sun.rmi.transport.SequenceEntry?from?D:\software\program-tool\javaSE1.8\jdk1.8\jre\lib\rt.jar]
[Loaded?java.util.concurrent.locks.LockSupport?from?D:\software\program-tool\javaSE1.8\jdk1.8\jre\lib\rt.jar]
[Loaded?sun.rmi.server.MarshalOutputStream$1?from?D:\software\program-tool\javaSE1.8\jdk1.8\jre\lib\rt.jar]


當(dāng)元空間經(jīng)過(guò)Full gc也不夠的時(shí)候,就會(huì)拋出異常。

-XX:MaxMetaspaceSize=4M 設(shè)置最大大小為4M,那么對(duì)于這個(gè)程序來(lái)說(shuō)肯定是不夠內(nèi)存的。運(yùn)行程序,打印結(jié)果如下圖:


可以發(fā)現(xiàn),已經(jīng)拋出異常。如果不是拋出這個(gè)異常,而是

Error occurred during initialization of VM MaxMetaspaceSize is too small.

那么可能你的版本比我這個(gè)java版本要小,因?yàn)檫@個(gè)最小值的檢查,在https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8024945 里面提到的版本之后就被移除了。


一般來(lái)說(shuō),元空間垃圾回收的機(jī)會(huì)比較小,因?yàn)樾遁d類的信息是非常嚴(yán)格的,所以你可以試下在jconsole中,那個(gè)執(zhí)行g(shù)c的按鈕,進(jìn)行g(shù)c的操作,但是你會(huì)發(fā)現(xiàn),基本沒(méi)有回收元空間的內(nèi)存,一般會(huì)觸發(fā)這里gc的原因,都是那個(gè)自定義加載器加載的那些類。比如cglib技術(shù)加載的類,自定義類加載器加載的類等。


cglib的測(cè)試代碼:這里你設(shè)置下-XX:MaxMetaspaceSize=14M -verbose:class 參數(shù),然后通過(guò)jconsole,可以看到元空間在不停增大,最后就拋出oom異常的現(xiàn)象!

package?jdk8;import?net.sf.cglib.proxy.Enhancer;import?net.sf.cglib.proxy.MethodInterceptor;import?java.util.concurrent.TimeUnit;public?class?CglibAddClass?{????public?static?void?main(String[]?args)?throws?InterruptedException{????????while?(true){
????????????TimeUnit.MILLISECONDS.sleep(100L);
????????????Enhancer?enhancer?=new?Enhancer();
????????????enhancer.setSuperclass(CglibAddClass.class);
????????????enhancer.setUseCache(false);
????????????enhancer.setCallback((MethodInterceptor)(obj,?method,?arg,?proxy)->
????????????????proxy.invokeSuper(obj,?arg));
????????????enhancer.create();
????????}
????}
}


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

相關(guān)閱讀更多精彩內(nèi)容

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