JVM 中的永久代
PermGen(永久代)
絕大部分 Java 程式設計師應該都見過 “java.lang.OutOfMemoryError: PermGen space “這個異常。這裡的 “PermGen space”其實指的就是方法區。不過方法區和“PermGen space”又有著本質的區別。前者是 JVM 的規範,而後者則是 JVM 規範的一種實現,並且只有 HotSpot 才有 “PermGen space”,而對於其他型別的虛擬機器,如 JRockit(Oracle)、J9(IBM) 並沒有“PermGen space”。由於方法區主要儲存類的相關資訊,所以對於動態生成類的情況比較容易出現永久代的記憶體溢位。最典型的場景就是,在 jsp 頁面比較多的情況,容易出現永久代記憶體溢位。我們現在通過動態生成類來模擬 “PermGen space”的記憶體溢位:
package com.paddx.test.memory;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
public class PermGenOomMock{
public static void main(String[] args) {
URL url = null;
List<ClassLoader> classLoaderList = new ArrayList<ClassLoader>();
try {
url = new File("/tmp").toURI().toURL();
URL[] urls = {url};
while (true){
ClassLoader loader = new URLClassLoader(urls);
classLoaderList.add(loader);
loader.loadClass("com.paddx.test.memory.Test");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
執行結果如下:
本例中使用的 JDK 版本是 1.7,指定的 PermGen 區的大小為 8M。通過每次生成不同URLClassLoader物件來載入Test類,從而生成不同的類物件,這樣就能看到我們熟悉的 “java.lang.OutOfMemoryError: PermGen space ” 異常了。這裡之所以採用 JDK 1.7,是因為在 JDK 1.8 中, HotSpot 已經沒有 “PermGen space”這個區間了,取而代之是一個叫做 Metaspace(元空間) 的東西。下面我們就來看看 Metaspace 與 PermGen space 的區別。
Metaspace(元空間)
其實,移除永久代的工作從JDK1.7就開始了。JDK1.7中,儲存在永久代的部分資料就已經轉移到了Java Heap或者是 Native Heap。但永久代仍存在於JDK1.7中,並沒完全移除,譬如符號引用(Symbols)轉移到了native heap;字面量(interned strings)轉移到了java heap;類的靜態變數(class statics)轉移到了java heap。我們可以通過一段程式來比較 JDK 1.6 與 JDK 1.7及 JDK 1.8 的區別,以字串常量為例:
package com.paddx.test.memory;
import java.util.ArrayList;
import java.util.List;
public class StringOomMock {
static String base = "string";
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for (int i=0;i< Integer.MAX_VALUE;i++){
String str = base + base;
base = str;
list.add(str.intern());
}
}
}
這段程式以2的指數級不斷的生成新的字串,這樣可以比較快速的消耗記憶體。我們通過 JDK 1.6、JDK 1.7 和 JDK 1.8 分別執行:
JDK 1.6 的執行結果:
JDK 1.7的執行結果:
JDK 1.8的執行結果:
從上述結果可以看出,JDK 1.6下,會出現“PermGen Space”的記憶體溢位,而在 JDK 1.7和 JDK 1.8 中,會出現堆記憶體溢位,並且 JDK 1.8中 PermSize 和 MaxPermGen 已經無效。因此,可以大致驗證 JDK 1.7 和 1.8 將字串常量由永久代轉移到堆中,並且 JDK 1.8 中已經不存在永久代的結論。現在我們看看元空間到底是一個什麼東西?
元空間的本質和永久代類似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機器中,而是使用本地記憶體。因此,預設情況下,元空間的大小僅受本地記憶體限制,但可以通過以下引數來指定元空間的大小:
-XX:MetaspaceSize,初始空間大小,達到該值就會觸發垃圾收集進行型別解除安裝,同時GC會對該值進行調整:如果釋放了大量的空間,就適當降低該值;如果釋放了很少的空間,那麼在不超過MaxMetaspaceSize時,適當提高該值。
-XX:MaxMetaspaceSize,最大空間,預設是沒有限制的。
除了上面兩個指定大小的選項以外,還有兩個與 GC 相關的屬性:
-XX:MinMetaspaceFreeRatio,在GC之後,最小的Metaspace剩餘空間容量的百分比,減少為分配空間所導致的垃圾收集
-XX:MaxMetaspaceFreeRatio,在GC之後,最大的Metaspace剩餘空間容量的百分比,減少為釋放空間所導致的垃圾收集
現在我們在 JDK 8下重新執行一下程式碼段 4,不過這次不再指定 PermSize 和 MaxPermSize。而是指定 MetaSpaceSize 和 MaxMetaSpaceSize的大小。輸出結果如下:
從輸出結果,我們可以看出,這次不再出現永久代溢位,而是出現了元空間的溢位。
相關文章
- jvm的永久代JVM
- JVM永久代JVM
- JVM - 方法區(永久代)的垃圾回收JVM
- 對jvm堆中年輕代,老年代,永久代的理解JVM
- 對JVM中垃圾回收機制的個人理解--新生代,老年代,永久代JVM
- JVM記憶體管理、JVM垃圾回收機制、新生代、老年代以及永久代JVM記憶體
- JVM垃圾回收——新生代,老年代,永久代,Minor GC,Full GCJVMGC
- 【深入理解JVM】方法區 永久代 元空間JVM
- 深入理解JVM虛擬機器1:JVM記憶體的結構與消失的永久代JVM虛擬機記憶體
- 關於jvm的永久代會發生垃圾垃圾回收嗎?進來便知JVM
- Java永久代去哪兒了Java
- 聊聊JVM的年輕代JVM
- 常用的jvm配置引數 :永久區引數配置JVM
- Java 虛擬機器永久代的垃圾收集Java虛擬機
- Java面試題虛擬機器為什麼使用元空間替換了永久代(JVM篇)Java面試題虛擬機JVM
- 輕鬆理解JVM的分代模型JVM模型
- JVM 年輕代和年老代 大小設定JVM
- jvm的新生代和老年代簡介JVM
- jvm垃圾分代回收演算法JVM演算法
- 如何永久刪除Mac中的檔案?Mac
- Java當中的JVMJavaJVM
- JVM 中的垃圾回收JVM
- JVM 中的StringTableJVM
- 3、JVM中的物件JVM物件
- JVM中堆的介紹JVM
- JDK8-廢棄永久代(PermGen)迎來元空間(Metaspace)JDK
- JVM 記憶體分代、垃圾回收漫談JVM記憶體
- Java8記憶體模型—永久代(PermGen)和元空間(Metaspace)Java記憶體模型
- JVM中ClassLoader的學習JVM
- Tomcat 中的JVM問題TomcatJVM
- 深入理解JVM——(三)為什麼JVM新生代需要兩個Survivor區JVM
- 如何在eclipse中的maven中永久修改jdk的版本號EclipseMavenJDK
- JVM中的執行緒行為JVM執行緒
- JDK中自帶的JVM分析工具JDKJVM
- 深入理解JVM中的ClassLoaderJVM
- JVM調優總結-分代垃圾回收詳述1JVM
- JVM調優總結-分代垃圾回收詳述2JVM
- Java虛擬機器:JVM記憶體分代策略Java虛擬機JVM記憶體