除了程序計數器外,虛擬機內存的其他幾個運行時區域都有發生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,就可以考慮下這個原因