1. 程式人生 > >JVM解讀(四):JVM記憶體溢位異常分析

JVM解讀(四):JVM記憶體溢位異常分析

JVM全稱是java Virtual Machine(java虛擬機器),JVM遮蔽了與各個計算機平臺相關的軟體和硬體差異。
在接下來的日子裡,通過寫部落格的形式學習JVM,讓自己更懂得Java!
本系列文章是對《深入分析javaweb技術內幕》和《深入理解java虛擬機器》的總結,歡迎大家一起吐槽,一起進步。
《JVM解讀》第一篇:JVM體系結構
《JVM解讀》第二篇:JVM類載入器ClassLoader
《JVM解讀》第三篇:JVM記憶體區域
《JVM解讀》第四篇:JVM記憶體溢位異常分析
《JVM解讀》第五篇:JVM垃圾收集

在實際的開發中我們可能會遇到各種各樣的記憶體溢位(OutOfMemoryError)問題,我在開發的時候就遇到過這樣的情況。當時是報的這樣異常

Caused by:java.lang.OutOfMemoryError:PermGen Space
  at java.lang.ClassLoader……

造成這個原因的是我們的tomcat下方了好幾個web程式,而且每個程式都有大量的Spring ,hibernate的jar包,並且這幾個程式的jar基本上都是一樣的。這就造成了重複載入的情況,直接導致我的永久區溢位。後來把公共的jar提出來放在一個share資料夾下,這樣就可以了。而且Spring,Hibernate,在對類進行增強時,都會使用到CGLib這類位元組碼技術,增強的類越多,就需要越大的方法區來保證動態生成的class可以載入如記憶體。

java堆溢位
java堆用於儲存物件,所有不斷的建立物件並且在GC Roots到物件之間有可達路徑避免垃圾回收清除這些物件,在物件數量達到最大堆的容量限制後就會產生堆溢位

public class HeapOOM {

    static class OOMObject{}

    public static void main(String[] args) {
        List<OOMObject> list=new ArrayList<HeapOOM.OOMObject>();
        while(true)
            list.add(new
OOMObject()); }

堆記憶體溢位

虛擬機器棧和本地方法棧溢位
棧容量的設定由引數-Xss引數設定。java虛擬機器規範中描述的異常

  • 如果執行緒請求的棧深度大於虛擬機器所執行的最大深度,將丟擲StackOverflowError
  • 如果虛擬機器在擴充套件時無法申請到足夠的記憶體空間,則丟擲OutOfMemoryError異常
public class JavaVMStackOF {

    private int stackLength=1;
    public void stackLeak()
    {
        stackLength++;
        stackLeak();
    }
    public static void main(String[] args) throws Throwable {
        JavaVMStackOF jvof=new JavaVMStackOF();
        try {
            jvof.stackLeak();
        } catch (Throwable e) {
            System.out.println("stack length:"+jvof.stackLength);
            throw e;
        }

    }

}

棧溢位
實驗表明:在當個執行緒下,無論由於棧幀太大還是虛擬機器棧容量太小,當記憶體無法分配時,虛擬機器丟擲的都是StackOverflowErroe異常
方法區和執行時常量池溢位
執行時 常量池是方法區的一部分。我們通過一個String.intern()方法來測試。String.intern()是一個本地方法,如果字串常量池中已經包含一個等於此String物件,則返回代表池中這個字串的String物件;否則將此String物件包含的字串新增到常量池中,並且返回該String物件的引用。

public class RuntimeConstantPoolOOM {
    public static void main(String[] args) {
        //使用list保持對常量池引用,避免Full GC回收常量池
        List<String> list=new ArrayList<String>();
        int i=0;
        while(true)
            list.add(String.valueOf(i++).intern());

    }

方法區溢位
這裡我一開始是用jre7的,結果沒反應,改為jre6才有產生這樣的情況。
JVM上的動態語言通常會持續建立類來實現語言的動態性,也會經常造成PermGen space異常

直接記憶體溢位
DirectMemory容量可通過-XX:MaxDirectMemorySize指定,如果不指定,預設與java堆的最大值-Xmx一樣。

public class DirectMemoryOOM {

    private static final int _1MB=1024*1024;

    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
        Field unsafeField=Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe=(Unsafe) unsafeField.get(null);
        while(true)
            unsafe.allocateMemory(_1MB);
    }

直接記憶體溢位
由DirectMemory導致的記憶體溢位,一個很明顯的特徵是在Heap Dump檔案中不會看見明顯的異常。如果發現OOM情況後Dump檔案很小,而程式中又使用了NIO,可以檢查是不是直接記憶體溢位。