OOM實戰 (一)

概念:

OOM,全稱“Out Of Memory”,翻譯成中文就是“內存用完了”,來源於java.lang.OutOfMemoryError。看下關於的官方說明: Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector. 意思就是說,當JVM因爲沒有足夠的內存來爲對象分配空間並且垃圾回收器也已經沒有空間可回收時,就會拋出這個error(注:非exception,因爲這個問題已經嚴重到不足以被應用處理)。

類別:

OOM主要分爲下面幾種:

  • 堆溢出
  • 虛擬機棧和本地方法棧溢出
  • 方法區和運行時常量池溢出
  • 本機直接內存溢出

下面來通過小demo來模擬一下出現情況

堆溢出:

堆中存儲的是對象或者數組,只要一直往堆中分配對象,就可以出現oom。爲了方便測試,在運行時加上一些參數:

-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError  該參數表示堆的最小內存和最大內存爲20m且不可擴展,在發生oom時自動生成快照文件,以便後續分析。

public class HeapOom {

    private int[] numbers = new int[1 * 1024*1024];

    public static void main(String[] args) throws InterruptedException {
        List<HeapOom> heapOoms = new ArrayList<>();
        while   (true){
            HeapOom heapOom = new HeapOom();
            heapOoms.add(heapOom);
        }
    }
}

看一下輸出結果:產生了oom,在oom的時候產生了堆的快照文件,以便後續分析。

虛擬機棧和本地方法棧溢出:

棧是由一個一個的棧楨組成的。一個方法調用的時候則會在虛擬機棧中創建一個棧楨,方法調用到結束的過程即爲棧楨的入棧和出棧的過程。hotspot虛擬機中的棧內存是無法自動擴容的。這樣的話,可能會有兩種情況造成棧內存溢出

1 方法的調用太深,會導致棧楨創建的太多,耗盡了當前線程分配的棧的內存。例如死循環。爲了方便測試,在運行時加上一些參數:

-Xss256k   該參數表示堆的最小內存和最大內存爲20m且不可擴展,在發生oom時自動生成快照文件,以便後續分析。

public class StackOverFlowTest {


    public static void main(String[] args){
        test();
    }

    public static void test(){
        test();
    }
}

運行結果:出現StackOverFlowError

2 棧楨的大小過於大,導致了耗盡了內存。那什麼情況會導致棧楨的大小大呢?棧是由程序計數器、局部變量表、操作數棧、動態鏈接組成。如果方法的局部變量很多的話,調用時創建的棧楨佔用的空間比較大,就有可能造成棧溢出。

/** 通過申明大量的局部變量導致棧楨的空間大,超過可申請的空間,造成StackOverFlow
 * -Xss256k
 */
public class StackOverFlowTest2 {


    public static void main(String[] args){
        test();
    }

    public static void test(){
        long long1,long2,long3,long4,long5,long6,long7,long8,long9,long10,
                long11,long12,long13,long14,long15,long16,long17,long18,long19,long20;
        long1 =long2 = long3=long4=long5=long6=long7=long8=long9=long10
                =long11=long12=long13=long14=long15=long16=long17=long18=long19=long20=0;
        test();
    }
}

注:*long佔8個字節,要佔到256k需要申明大量的局部變量,所以還是通過死循環的方法調用,如果調用次數變少也可間接的證明棧楨過大無法申請空間時會造成stackOverFlow。至於如何證明調用次數減少,可以定義一個靜態變量,每次進入test方法時+1,由於oom程序會停止,所以可以通過try{}catch(Error e){} 來獲得最終的調用次數,類似於下面這個:

方法區和運行時常量池溢出:

首先要知道方法區中存放的是什麼:存放已被虛擬機加載的類的信息,常量、靜態變量。

方法區溢出:

由於動態代理是通過生成代理類,然後加載到jvm中,所以可以通過不斷的生成代理類來模擬方法區溢出

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章