故障原因
OutOfMemoryError
內存溢出錯誤是因爲內存空間不足導致分配內存空間失敗而拋出的異常。可能導致該問題的原因有兩個:
- 內存泄漏,存在對象資源未被回收,導致內存空間不足,需要分析內存當中的對象資源佔用情況。
- JVM設置的內存空間不足,需要考慮提高內存空間。
故障代碼
內存溢出錯誤極容易重現,只要寫一個死循環即可。
package com.example.demo;
import java.util.ArrayList;
import java.util.List;
public class App {
static class OOMObject {}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<App.OOMObject>();
while(true) {
list.add(new OOMObject());
}
}
}
考慮到內存耗盡時容易造成死機,在測試重現該問題時,建議通過添加JVM啓動參數
限制內存的分配上限。如下:
-Xms2M -Xmn1M
這裏設置了堆內存爲2M,新生代內存爲1M。
內存分析
當系統出現內存溢出故障時,需要生成內存快照,以此作爲內存分析的依據。通過添加JVM參數,即可在系統拋出內存溢出錯誤時,生成當時的內存快照。
-XX:+HeapDumpOnOutOfMemoryError
最後運行程序,在系統拋出OutOfMemoryError異常後,可在項目根目錄下發現文件java_pid9104.hprof。內存快照的分析,需要藉助內存分析工具:比較常用的有這幾款:
JProfiler
,這一款是商用軟件,應該不賴,但是作爲學習之用就不推薦了。Eclipse Memory Analizer
,這是一款開源的內存分析工具,但是由於生成的快照文件比較大(3.23G),用這個工具根本打不開,哪怕調整了啓動參數也不行,放棄。VisualVM
,如果用的是JDK8
,自帶了這款分析工具;如果是JDK9
及其以上,需要單獨下載使用。
因爲我是通過JDK9生成的內存快照,故單獨下載安裝了VisualVM,進行內存快照的分析。
通過Summary,可以看到幾個需要着重關鍵的點:
- 拋出內存溢出異常的是哪個線程
- 哪個類和實例內存佔用比較多
- 佔用內存較多的類或者實例,其對應的
GC Root
是什麼
通過上面幾個如果,可以點擊view all
查看詳情,就會對故障的位置有個大致的猜想。當然,演示的例子比較簡單,正式環境就需要多花一些時間去檢查和驗證了。