一、什麼是內存溢出?
內存溢出(OOM:out of memory)通俗理解就是內存不夠,通常在運行大型軟件或遊戲時,軟件或遊戲所需要的內存遠遠超出了你主機內安裝的內存所承受大小,就叫內存溢出。
在Java中,將會產生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?
爲什麼會沒有內存了呢?原因不外乎有兩點:
分配的少了:比如虛擬機本身可使用的內存(一般通過啓動時的VM參數指定)太少。
應用用的太多,並且用完沒釋放,浪費了。此時就會造成內存泄露或者內存溢出。
在Java語言中,由於存在了垃圾自動回收機制,所以,我們一般不用去主動釋放不用的對象所佔的內存,也就是理論上來說,是不會存在“內存泄露”的。但是,如果編碼不當,比如,將某個對象的引用放到了全局的Map中,雖然方法結束了,但是由於垃圾回收器會根據對象的引用情況來回收內存,導致該對象不能被及時的回收。如果該種情況出現次數多了,就會導致內存溢出,比如系統中經常使用的緩存機制。Java中的內存泄露,不同於C++中的忘了delete,往往是邏輯上的原因泄露。
三、如何分析Java OOM?
在故障定位(尤其是out of memory)和性能分析的時候,經常會用到一些文件來幫助我們排除代碼問題。這些文件記錄了JVM運行期間的內存佔用、線程執行等情況,這就是我們常說的dump文件。常用的有heap dump和thread dump(也叫javacore,或java dump)。我們可以這麼理解:heap dump記錄內存信息的,thread dump是記錄CPU信息的。這裏我們重點介紹heap dump。
heap dump文件是一個二進制文件,它保存了某一時刻JVM堆中對象使用情況。HeapDump文件是指定時刻的Java堆棧的快照,是一種鏡像文件。Heap Analyzer工具通過分析HeapDump文件,哪些對象佔用了太多的堆棧空間,來發現導致內存泄露或者可能引起內存泄露的對象。
四、案例
首先,我們來開發一段Java程序。
import java.util.*;public clas Test { public static void main(String[] args) { List<String> list = new ArrayList<String>(); int i = 0; while (true) { list.add(new String("test")); } } }
使用下面的命令運行該程序時設置JVM的堆內存(heap size)的極限值爲10M(-Xmx10m)。
java -Xmx10m Test
很快,程序將會產生OOM的錯誤,如下圖所示:
五、如何生成Head Dump文件?
我們可以在運行Java程序的時候,加入下面的參數:
-XX:+HeapDumpOnOutOfMemoryError
此參數是幫助生成dump文件,程序啓動後直到拋出OOM異常。異常拋出後,在程序的claspath下會生成以一個以.hprof結尾的文件,如:java_pid4504.hprof,這就是我們需要的dump文件。
如下圖所示:
六、使用IBM heapAnalyzer分析Head Dump文件
IBM heapAnalyzer(https://www.ibm.com/support/pages/ibm-heapanalyzer)是IBM開發的強大的內存dump分析處理工具,,IBM heapAnalyzer是通過分析OOM後的Java heap dump文件的,通過對dump文件的分析找到內存可能泄露的點。
啓動IBM heapAnalyzer,並導入剛纔生成的Heap Dump文件,如下圖所示。
通過分析我們會發現,系統94.19%的內存都被一個ArrayList佔用了(裏面保存的都是Object)。這裏就有可能是一個內存的溢出點。當然,我們這個例子非常典型,在實際工作可能沒有這麼明顯,需要具體問題具體分析。