我這里使用的版本為: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();
????????}
????}
}