JDK8-廢棄永久代(PermGen)迎來元空間(Metaspace)

1、背景

1.1 永久代(PermGen)在哪裏?

根據,hotspot jvm結構以下(虛擬機棧和本地方法棧合一塊兒了):java

上圖引自網絡,但有個問題:方法區和heap堆都是線程共享的內存區域。緩存

關於方法區和永久代:服務器

在HotSpot JVM中,此次討論的永久代,就是上圖的方法區(JVM規範中稱爲方法區)。《Java虛擬機規範》只是規定了有方法區這麼個概念和它的做用,並無規定如何去實現它。在其餘JVM上不存在永久代。

網絡

1.2 JDK8永久代的廢棄

JDK8 永久代變化以下圖:app

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

2.老年代:OldGenjvm

3.永久代(方法區的實現) : PermGen----->替換爲Metaspace(本地內存中)測試

 

 2、爲何廢棄永久代(PermGen)

 2.1 官方說明

參照JEP122:http://openjdk.java.net/jeps/122,原文截取:url

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.spa

 即:移除永久代是爲融合HotSpot JVM與 JRockit VM而作出的努力,由於JRockit沒有永久代,不須要配置永久代。

 2.2 現實使用中易出問題

因爲永久代內存常常不夠用或發生內存泄露,爆出異常java.lang.OutOfMemoryError: PermGen

3、深刻理解元空間(Metaspace)

3.1元空間的內存大小

元空間是方法區的在HotSpot jvm 中的實現,方法區主要用於存儲類的信息、常量池、方法數據、方法代碼等。方法區邏輯上屬於堆的一部分,可是爲了與堆進行區分,一般又叫「非堆」。

元空間的本質和永久代相似,都是對JVM規範中方法區的實現不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機中,而是使用本地內存。,理論上取決於32位/64位系統可虛擬的內存大小。可見也不是無限制的,須要配置參數。

3.2經常使用配置參數

1.MetaspaceSize

初始化的Metaspace大小,控制元空間發生GC的閾值。GC後,動態增長或下降MetaspaceSize。在默認狀況下,這個值大小根據不一樣的平臺在12M到20M浮動。使用Java -XX:+PrintFlagsInitial命令查看本機的初始化參數

2.MaxMetaspaceSize

限制Metaspace增加的上限,防止由於某些狀況致使Metaspace無限的使用本地內存,影響到其餘程序。在本機上該參數的默認值爲4294967295B(大約4096MB)。

3.MinMetaspaceFreeRatio

當進行過Metaspace GC以後,會計算當前Metaspace的空閒空間比,若是空閒比小於這個參數(即實際非空閒佔比過大,內存不夠用),那麼虛擬機將增加Metaspace的大小。默認值爲40,也就是40%。設置該參數能夠控制Metaspace的增加的速度,過小的值會致使Metaspace增加的緩慢,Metaspace的使用逐漸趨於飽和,可能會影響以後類的加載。而太大的值會致使Metaspace增加的過快,浪費內存。

4.MaxMetasaceFreeRatio

當進行過Metaspace GC以後, 會計算當前Metaspace的空閒空間比,若是空閒比大於這個參數,那麼虛擬機會釋放Metaspace的部分空間。默認值爲70,也就是70%。

5.MaxMetaspaceExpansion

Metaspace增加時的最大幅度。在本機上該參數的默認值爲5452592B(大約爲5MB)。

6.MinMetaspaceExpansion

Metaspace增加時的最小幅度。在本機上該參數的默認值爲340784B(大約330KB爲)。

3.3測試並追蹤元空間大小

 3.3.1.測試字符串常量

 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 }

在eclipse中選中類--》run configuration-->java application--》new 參數以下:

 因爲設定了最大內存20M,很快就溢出,以下圖:

 可見在jdk8中:

1.字符串常量由永久代轉移到堆中。

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

3.3.2.測試元空間溢出

根據定義,咱們以加載類來測試元空間溢出,代碼以下:

 

 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:OOMTest
14  * @Description:模擬類加載溢出(元空間oom)
15  * @author diandian.zhang
16  * @date 2017年4月27日上午9:45:40
17  */
18 public class OOMTest {  
19     public static void main(String[] args) {  
20         try {  
21             //準備url  
22             URL url = new File("D:/58workplace/11study/src/main/java/jdk8").toURI().toURL();  
23             URL[] urls = {url};  
24             //獲取有關類型加載的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                 //顯示數量信息(共加載過的類型數目,當前還有效的類型數目,已經被卸載的類型數目)  
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 }  

 

爲了快速溢出,設置參數:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=80m,運行結果以下:

 

 上圖證明了,咱們的JDK8中類加載(方法區的功能)已經不在永久代PerGem中了,而是Metaspace中。能夠配合JVisualVM來看,更直觀一些。

 4、總結

本文講解了元空間(Metaspace)的由來和本質,經常使用配置,以及監控測試。元空間的大小是動態變動的,但不是無限大的,最好也時常關注一下大小,以避免影響服務器內存。

 

==================

參考:

深刻探究JVM(2) - 探祕Metaspace

【理解JVM】JVM內存模型

相關文章
相關標籤/搜索