JVM內存溢出查詢java.lang.OutOfMemoryError: Java heap space查出具體原因分為幾個預備步驟
1、在運行java程序是必須設置jvm
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/dump_OOME.hprof 當內存溢出時 會輸出在/tmp/dump_OOME.hprof目錄下
#jvm options #JAVA_OPTS="-Xms8192m -Xmx8192m -Xmn4096m -Djava.awt.headless=true -XX:MaxPermSize=512m -server -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=85 -XX:+DisableExplicitGC -Xnoclassgc -Xverify:none" JAVA_OPTS="-Xms4096m -Xmx4096m -Djava.awt.headless=true -XX:MaxPermSize=512m -server -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=85 -XX:+DisableExplicitGC -Xnoclassgc -Xverify:none" JAVA_OPTS="$JAVA_OPTS -javaagent:/usr/local/src/sflowagent.jar -Dsflow.hostname=platform -Dsflow.dsindex=10000" JAVA_OPTS="$JAVA_OPTS -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc:/tmp/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M" JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/dump_OOME.hprof"
2、可以在eclipse安裝分析dump_OOME.hprof 的插件MemoryAnalyzerTool
MemoryAnalyzerTool(也叫MAT)是一款JAVA虛擬機內存映像分析工具,
可以在JAVA程序運行的時候有程序拋出的異常加上已經設置好的參數(-XX:+HeapDumpOnOutOfMemory)調試出內存泄漏或者異常的位置以及原因跟蹤,
MemeoryAnalyzer可以對Dump出來的堆轉儲快照進行分析,重點是確認內存中的對象是否是有必要的,也就是要先確認到底是出現了內存泄漏(Memory Leak)還是內存溢出(Memory OverFlow)。
運行Eclipse,然后單擊菜單欄上“Help=》Install New Software...”

進入軟件源導入界面,你可以選擇使用URL直接導入,URL導入的地址為:
http://download.eclipse.org/mat/1.7/update-site/
但是由於鏡像連接不穩定性,我使用的是下載壓縮包zip然后安裝,壓縮包下載地址為:http://ftp.jaist.ac.jp/pub/eclipse/mat/1.7/MemoryAnalyzer-1.7.0.201706130745.zip
下載完成之后將其導入,"Add》Archive》選擇你下載好的zip文件》OK":

確定之后勾選"Memory Analyzer for Eclipse IDE"下面的兩項即可,這個是用於Eclipse內部使用的插件,下面一個“Stand-alone Memory Analyzer“是獨立的分析工具,一般不用勾選。

之后單擊“Next"等待一段時間Eclipse計算的需求和依賴文件下載完畢后,一路"next",並接受協議“Finish”即可完成安裝,重啟Eclispe后就可以使用MAT了。
3、使用Memory Analyzer 分析內存溢出
1.設置JVM參數
JVM的參數可以用於調試JVM在各種限制條件下的常見BUG,一下簡單介紹幾個參數:
-XX:+HeapDumpOnOutOfMemoryError //虛擬機在堆異常時生成對存儲快照,后綴一般是.hprof -Xms //Java堆的最小值,例如-Xms20m,將Java堆的最小值設置為20MB -Xmx //Java堆的最大值,例如-Xms40m,將Java堆的最大值設置為40MB
了解了參數之后,可以對參數進行一定的設置,以便我們簡單的對MAT工具進行使用測試,設置參數如下:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=D://dump_OOME.hprof -Xms20m -Xmx20m
之所以將最小和最大的堆設置為一樣是為了能避免堆的自動擴展,以及20MB的空間是為了讓程序能盡快的占滿使得內存溢出。
設置JVM的啟動參數可在“Run》Run Configuration》Arguments》Vm arguments”中填寫

2.編寫內存溢出(Memory Leak)代碼
思路:通過新建集合列表List,然后以while(true)死循環的方式使得List集合不斷添加新的對象,使得其不斷地占滿堆空間
代碼:
package com.study;
import java.util.ArrayList;
import java.util.List;
public class TestStudy {
static class OOMClass { }
public static void main(String[] args) {
List<OOMClass> list = new ArrayList<OOMClass>();
while(true) {
list.add(new OOMClass());
}
}
}
執行代碼,發現輸出如下:

注意到“java_pid24792.hprof”文件,這個文件是JVM生成的對存儲快照,之后可以通過MAT工具打開,進行分析追蹤,這個文件將自動保存到你的Eclipse workspace下的工作目錄中,以我的為例:

3.打開MAT的Perspective
由於MAT工具需要另外打開一個工作視圖(就像Eclipse EE和Java的切換視圖一樣),因此需要在先配置打開"Window》Perspective》Open Perspective》Other...":

選擇Memory Analysis並確定:

完后之后將進入MAT的工作空間視圖,你可以在Eclipse右上角進行切換回Java或者EE,由於接下來要使用MAT分析,因此就暫時不切回之前的Java了
![]()
4.打開需要分析的堆轉儲快照.hprof文件
由於MAT是依賴於生成的堆轉儲快照,而不是在運行的時候自動捕獲,因此,再確認進入MAT的工作空間后,點擊"File》Open Heap Dump》選擇剛生成的“java_pid24792.hprof”文件,一直默認就可以查看內存問題的分析圖表以及GC根

你可以進行一些分析操作,可以看到Action下的幾個選項:
a.Histogram:列出每個類的實例列表;
b.Dominator:列出最大的對象和他們存在的東西;
c.Top Comsumer:打印按類和包分組的最昂貴的對象;
d.Duplicate Classes:檢測由多個類加載器加載的類。
5.使用MAT找出問題存在的對象
由於是一個簡單的問題,因此我們直接選擇Dominator,可以直接以百分比的形式打印出對象所占的百分比,一般的內存溢出大部分情況都是有某一個對象過多存在而未被GC回收導致,所以可以點開最大百分比的類一級級找,不難找到問題所在:

可以看到列出額其中的25條,還有810300條目未列出,這81萬個對象占據了97.36%的堆空間,因此不難發現是OOMClass類的對象生成過多為未被回收導致內存溢出。
6.進一步追蹤問題源並進行定位
在"default_report org.eclipse.mat.api:suspects"的標簽分頁中

在"Program Suspect 1》Details"點擊后找到"Thread Stack"中追蹤出錯點

看到出錯點在第13行,找到對應的Java代碼TestStudy的13行:
1 package com.study;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 public class TestStudy {
7
8 static class OOMClass { }
9
10 public static void main(String[] args) {
11 List<OOMClass> list = new ArrayList<OOMClass>();
12 while(true) {
13 list.add(new OOMClass());
14 }
15 }
16
17 }
找到了追蹤的出錯位置,以上就是使用Memory Analyzer工具完成的堆棧分析。

