一、背景

1.1 永久代(PermGen)在哪里?

根據(jù),hotspot jvm結(jié)構(gòu)如下(虛擬機棧和本地方法棧合一起了):

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

上圖引自網(wǎng)絡(luò),但有個問題:方法區(qū)和heap堆都是線程共享的內(nèi)存區(qū)域。

關(guān)于方法區(qū)和永久代:

在HotSpot JVM中,這次討論的永久代,就是上圖的方法區(qū)(JVM規(guī)范中稱為方法區(qū))?!禞ava虛擬機規(guī)范》只是規(guī)定了有方法區(qū)這么個概念和它的作用,并沒有規(guī)定如何去實現(xiàn)它。在其他JVM上不存在永久代。

1.2 JDK8永久代的廢棄

JDK8 永久代變化如下圖:

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

1.新生代:Eden+From Survivor+To Survivor

2.老年代:OldGen

3.永久代(方法區(qū)的實現(xiàn)) : PermGen----->替換為Metaspace(本地內(nèi)存中)

 

 二、為什么廢棄永久代(PermGen)

 2.1 官方說明

參照J(rèn)EP122:http://openjdk.java.net/jeps/122,原文截取:

Motivation

This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.

 即:移除永久代是為融合HotSpot JVM與 JRockit VM而做出的努力,因為JRockit沒有永久代,不需要配置永久代。

 2.2 現(xiàn)實使用中易出問題

由于永久代內(nèi)存經(jīng)常不夠用或發(fā)生內(nèi)存泄露,爆出異常java.lang.OutOfMemoryError: PermGen

三、深入理解元空間(Metaspace)

3.1元空間的內(nèi)存大小

元空間是方法區(qū)的在HotSpot jvm 中的實現(xiàn),方法區(qū)主要用于存儲類的信息、常量池、方法數(shù)據(jù)、方法代碼等。方法區(qū)邏輯上屬于堆的一部分,但是為了與堆進(jìn)行區(qū)分,通常又叫“非堆”。

元空間的本質(zhì)和永久代類似,都是對JVM規(guī)范中方法區(qū)的實現(xiàn)。不過元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機中,而是使用本地內(nèi)存。,理論上取決于32位/64位系統(tǒng)可虛擬的內(nèi)存大小。可見也不是無限制的,需要配置參數(shù)。

3.2常用配置參數(shù)

1.MetaspaceSize

初始化的Metaspace大小,控制元空間發(fā)生GC的閾值。GC后,動態(tài)增加或降低MetaspaceSize。在默認(rèn)情況下,這個值大小根據(jù)不同的平臺在12M到20M浮動。使用Java -XX:+PrintFlagsInitial命令查看本機的初始化參數(shù)

2.MaxMetaspaceSize

限制Metaspace增長的上限,防止因為某些情況導(dǎo)致Metaspace無限的使用本地內(nèi)存,影響到其他程序。在本機上該參數(shù)的默認(rèn)值為4294967295B(大約4096MB)。

3.MinMetaspaceFreeRatio

當(dāng)進(jìn)行過Metaspace GC之后,會計算當(dāng)前Metaspace的空閑空間比,如果空閑比小于這個參數(shù)(即實際非空閑占比過大,內(nèi)存不夠用),那么虛擬機將增長Metaspace的大小。默認(rèn)值為40,也就是40%。設(shè)置該參數(shù)可以控制Metaspace的增長的速度,太小的值會導(dǎo)致Metaspace增長的緩慢,Metaspace的使用逐漸趨于飽和,可能會影響之后類的加載。而太大的值會導(dǎo)致Metaspace增長的過快,浪費內(nèi)存。

4.MaxMetasaceFreeRatio

當(dāng)進(jìn)行過Metaspace GC之后, 會計算當(dāng)前Metaspace的空閑空間比,如果空閑比大于這個參數(shù),那么虛擬機會釋放Metaspace的部分空間。默認(rèn)值為70,也就是70%。

5.MaxMetaspaceExpansion

Metaspace增長時的最大幅度。在本機上該參數(shù)的默認(rèn)值為5452592B(大約為5MB)。

6.MinMetaspaceExpansion

Metaspace增長時的最小幅度。在本機上該參數(shù)的默認(rèn)值為340784B(大約330KB為)。

3.3測試并追蹤元空間大小

 3.3.1.測試字符串常量

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

 1 public class StringOomMock { 2     static String  base = "string"; 3      4     public static void main(String[] args) { 5         List<String> list = new ArrayList<String>(); 6         for (int i=0;i< Integer.MAX_VALUE;i++){ 7             String str = base + base; 8             base = str; 9             list.add(str.intern());10         }11     }12 }

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

在eclipse中選中類--》run configuration-->java application--》new 參數(shù)如下:

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

 由于設(shè)定了最大內(nèi)存20M,很快就溢出,如下圖:

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

 可見在jdk8中:

1.字符串常量由永久代轉(zhuǎn)移到堆中。

2.持久代已不存在,PermSize MaxPermSize參數(shù)已移除。(看圖中最后兩行)

3.3.2.測試元空間溢出

根據(jù)定義,我們以加載類來測試元空間溢出,代碼如下:

 

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

 1 package jdk8; 2  3 import java.io.File; 4 import java.lang.management.ClassLoadingMXBean; 5 import java.lang.management.ManagementFactory; 6 import java.net.URL; 7 import java.net.URLClassLoader; 8 import java.util.ArrayList; 9 import java.util.List;10 11 /**12  * 
13  * @ClassName:OOMTest14  * @Description:模擬類加載溢出(元空間oom)15  * @author diandian.zhang16  * @date 2017年4月27日上午9:45:4017  */18 public class OOMTest {  
19     public static void main(String[] args) {  
20         try {  
21             //準(zhǔn)備url  22             URL url = new File("D:/58workplace/11study/src/main/java/jdk8").toURI().toURL();  
23             URL[] urls = {url};  
24             //獲取有關(guān)類型加載的JMX接口  25             ClassLoadingMXBean loadingBean = ManagementFactory.getClassLoadingMXBean();  
26             //用于緩存類加載器  27             List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();  
28             while (true) {  
29                 //加載類型并緩存類加載器實例  30                 ClassLoader classLoader = new URLClassLoader(urls);  
31                 classLoaders.add(classLoader);  
32                 classLoader.loadClass("ClassA");  
33                 //顯示數(shù)量信息(共加載過的類型數(shù)目,當(dāng)前還有效的類型數(shù)目,已經(jīng)被卸載的類型數(shù)目)  34                 System.out.println("total: " + loadingBean.getTotalLoadedClassCount());  
35                 System.out.println("active: " + loadingBean.getLoadedClassCount());  
36                 System.out.println("unloaded: " + loadingBean.getUnloadedClassCount());  
37             }  
38         } catch (Exception e) {  
39             e.printStackTrace();  
40         }  
41     }  
42 }

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

 

為了快速溢出,設(shè)置參數(shù):-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=80m,運行結(jié)果如下:

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

 

 上圖證實了,我們的JDK8中類加載(方法區(qū)的功能)已經(jīng)不在永久代PerGem中了,而是Metaspace中??梢耘浜螶VisualVM來看,更直觀一些。

 四、總結(jié)

本文講解了元空間(Metaspace)的由來和本質(zhì),常用配置,以及監(jiān)控測試。元空間的大小是動態(tài)變更的,但不是無限大的,最好也時常關(guān)注一下大小,以免影響服務(wù)器內(nèi)存。

------------------ 本人實力有限,理解有誤之處大家一定要提出來,多多發(fā)言討論。共同提高! ---------------

http://www.cnblogs.com/dennyzhangdd/p/6770188.html