《深入理解java虛擬機》讀書筆記——OOM發生的原因及解決方案

除了程序計數器外,虛擬機內存的其他幾個運行時區域都有發生OOM的可能,如果能夠區分根據報錯區分出是哪些區域報出來的異常,會更便於定位問題,解決問題。

一、java堆溢出

java.lang.OutOfMemoryError:java heap space

原因:產生大量不會被垃圾回收機制清除的對象(GC Roots到對象之間有可達路徑)

解決方案:先通過內存映像分析工具對Dump出來的堆轉儲快照進行分析,確認到底是發生了內存泄露(Memory Leak)還是內存溢出(Memory Overflow),如果是內存泄露,查看泄露對象到GC Roots的引用鏈,定位出泄露代碼的位置。如果是內存溢出,那麼就可以檢查堆參數(-Xms 與 -Xmx),與機器物理內存對比看是否還可以調大,從代碼上檢查是否存在某些對象生命週期過長、持有狀態時間過長的情況。

關於內存溢出與內存泄露的區別:

內存溢出是指申請的內存空間比剩餘的內存空間要大,比如剩餘1M的空間,但是實例化了一個2M的對象,就發生了內存溢出。內存泄露是指程序在申請了空間之後,無法釋放已申請的空間,而這部分空間程序也訪問不到,一次內存泄露可以忽視,但是內存泄露的堆積,最終也會造成內存溢出。(我的理解就是看內存中的對象是不是必要的,是必要的那就是內存溢出,不是必要的也沒辦法清除他的,就是內存泄露,需要找泄露的代碼)

看這個例子:

public class HeapOOM {
    static class OOMObject{

    }

    public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<>();
        while(true){
            list.add(new OOMObject());
        }
    }
}
jvm參數是:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=E:\JAVA\dump

因爲堆內存就設置了20m,所以啓動程序後很快能報OOM


打開jdk/bin/jvisualvm.exe,然後在這個可視化的界面中打開剛剛保存的內存堆轉儲快照,可以看到


HeapOOM類的內部類OOMObject的實例化對象幾乎佔滿了整個堆內存,所以也就查到了堆內存溢出的原因,當然實際的沒這麼簡單,這裏只是簡單的說明下。

,


二、虛擬機棧和本地方法棧溢出

由於Hot Spot虛擬機的實現是不區分兩者的,所以可以通過-Xss參數來設定棧容量。

1、java.lang.StackOverflowError

原因:在單線程下,虛擬機棧容量太小或者棧幀太大,會拋出SO;

解決方法:增大虛擬機棧容量;看看是不是定義了大量的本地變量,進行優化;可能是遞歸沒有出口導致方法的棧深度過大,給遞歸補上出口。

2、java.lang.OutOfMemoryError: unable to create new native thread

原因:在多線程下,大量創建新線程,會拋出OOM,每個線程的棧分配的內存越大,越容易產生;

解決方法:減少線程產生、減少最大堆、減少棧容量;


三、運行時常量池溢出

1、java.lang.OutOfMemoryError: PermGen space
                   at java.lang.String.intern(Native Method)

  原因:在jdk1.6及之前的版本中,代碼在運行時創建了大量的常量,超出了常量池上限;

  解決方法:在jdk1.6及之前的版本中,可以通過修改-XX:PermSize和-XX:MaxPermSize參數來修改方法區大小,從而修改常量池大小;


2、java.lang.OutOfMemoryError: Java heap space
原因:在jdk1.7及以後的版本把常量池從方法區移到了堆中,代碼運行時創建了大量的常量,造成了堆內存溢出

解決方法:通過修改-Xmx、-Xms來修改堆內存的大小。


四、方法區溢出

 java.lang.OutOfMemoryError: PermGen space
at java.lang.ClassLoader.defineClass(Native Method)


  原因:在運行時,ClassLoader動態加載了大量的Class信息,超出方法區上限;
  解決方法:通過修改-XX:PermSize和-XX:MaxPermSize參數來修改方法區大小;


五、本機直接內存溢出

java.lang.OutOfMemoryError

      at sun.misc.Unsafe.allocateMemory(Native Method)

      at com.bckj.OOM.DirectMemoryOOM.main(DirectMemoryOOM.java:18)

直接內存溢出,一個明顯的特徵就是在Heap Dump文件中不會看見明顯的異常,若發現OOM後Dump文件很小,而程序中又直接或間接的使用了NIO,就可以考慮下這個原因


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