模擬實戰中排查堆內存溢出(java.lang.OutOfMemoryError: Java heap space)的問題。java
堆內存溢出的緣由:通常都是建立了大量的對象,這些對象一直被引用着,沒法被GC垃圾回收掉,最終致使堆內存被佔滿,沒有足夠的空間存放新建立的對象時,就會出現堆內存溢出問題。程序員
在實際的業務場景中出現內存溢出的問題,排查起來通常是十分困難繁瑣的,本文將經過結合一個簡單的實例來闡述排查的具體思路和步驟。shell
注意:在實際場景中,通常都是部署在Linux服務器中的項目報出內存溢出的問題;爲了儘量還原出實際場景,本文也是將提早編寫好的能夠觸發內存溢出的代碼並打包成可運行的Jar包,而後放到服務器中執行的。小程序
// 建立一個Java類
public class OutOfMemory {
private String test;
public OutOfMemory(String test){
this.test = test;
}
}
// 模擬內存溢出的發生
public class TestOOM {
public static void main(String[] args) {
List<OutOfMemory> list = new ArrayList<OutOfMemory>();
while(true){
/** * 無限建立OutOfMemory對象,直至將堆空間佔滿,而且建立的OutOfMemory對象一直被list集合對象引用着, * 致使GC也沒法回收,最終出現堆內存溢出問題 */
list.add(new OutOfMemory("5656"));
System.out.println("5656");
}
}
}
複製代碼
代碼編寫完成後,使用開發工具導出可運行的Jar包— (TestOOM.jar) |
能夠直接使用centos或者Red Hat等均可以;windows
①、可以使用xshell、xftp工具將可運行的Jar包(Jar包叫:TestOOM.jar)放入到服務器中;
②、使用命令執行Jar包;命令:
java -Xms40m -Xmx70m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/tmp -jar TestOOM.jar
centos
注意:爲了儘快模擬發生堆內存溢出,因此在啓動Jar包時,設置了一些參數;參數解析:
1)、 -Xms40m 初始堆大小設置爲40m
2)、 -Xmx70m 最大堆大小設置爲70m
3)、 -XX:+HeapDumpOnOutOfMemoryError
出現堆內存溢出時,自動導出堆內存 dump 快照
4)、 -XX:HeapDumpPath=/usr/tmp 設置導出的堆內存快照的存放地址爲 /usr/tmp服務器
①、jps命令:此命令是用來查詢與Java相關的進程的,並輸出進程號;下圖就是展現上面運行的Jar包的進程號: 工具
②、jmap命令: jmap -heap 3324 此命令是查詢出進程號爲 3324 的JVM中堆內存信息;以下圖: 性能
在圖中能夠發現堆內存中新生代、年老代中 free 可用空間愈來愈小,這預示着即將會發生GC垃圾回收,從而使堆騰出更多的空間存放新建立的對象。學習
③、jstat命令:使用其監控JVM的性能信息;例如:在本次排查內存溢出的問題中,會使用 jstat 命令監控 JVM的 GC垃圾回收的狀況; 命令: jstat -gcutil 3324 1000 意思是每1000毫秒查詢一次進程號爲3324 的JVM的GC垃圾回收的狀況;以下圖:
(1)、 YGC(堆中新生代GC)、FGC( FULL GC)爲何觸發頻率這麼快呢?
答:因爲堆內存空間不夠用了,須要經過GC垃圾回收將一些空間進行回收,用於存放新建立的對象。
( 2)、當堆內存空間不夠用時,GC具體會發生什麼呢?
答:
1)、當堆中的新生代空間不夠用時,會觸發YGC,對堆中新生代空間進行垃圾回收,同時垃圾回收後剩餘存活的對象會移動到堆中老年代存儲,因此每次YGC後,堆中年老代中存儲的對象數量會增大;
2)、當堆的新生代即將發生YGC時,若是發現新生代中存活下來的對象比堆中年老代中剩餘的可用空間大的話,就會直接不進行YGC,而會直接觸發FGC,FULLGC會對整個堆空間(新生代、老年代)GC會對整個堆空間(新生代、老年代)以及方法區/永久代進行垃圾回收;
擴展:堆的結構圖 |
①、使用XFTP等工具將服務器中的快照文件導出,堆內存快照文件是以 hprof 爲後綴的文件;導出快照文件後,能夠經過JDK自帶的 jvisualvm.exe 分析工具打開進行分析。
jvisualvm.exe 是在哪裏呢?(以 windows 系統爲例) 它是在JDK的安裝目錄中的bin目錄下的。
如圖:
②、使用 jvisualvm.exe 導入快照文件,如圖: (1)、
(2)、 (3)、經過分析堆內存快照獲得的結論: 經過第(3)張圖,能夠發現堆內存中有一個實例對象的佔比爲 99.9%,能夠肯定是因爲這個實例對象大量建立致使堆內存的溢出; 說到這,能夠回過頭去看下咱們本身編寫的能夠觸發堆內存溢出的小程序,發現正是因爲在 while(true) 死循環 中無線建立 OutOfMemory對象,致使堆內存空間被耗盡。
結語:
經過上面的實戰小例子,咱們能夠大致瞭解到在出現堆內存溢出時的排查步驟,可是在實際的場景中,這種狀況可能會更加的複雜多變;好比說,上面的那個小例子在出現的堆內存溢出時自動生成的堆內存快照文件大小就達到了100多m,若是在實際的場景中,這個多是很是巨大的,這時可能就會發生快照分析工具沒法導入堆內存快照。因此說,咱們須要在平時經過不斷的學習,才能在將來出現問題時,能儘快定位問題並解決問題;程序員不光是能編寫好代碼,還須要有解決問題的能力。
一切看文章不點贊都是「耍流氓」,嘿嘿ヾ(◍°∇°◍)ノ゙!開個玩笑,動一動你的小手,點贊就完事了,你每一個人出一份力量(點贊 + 評論)就會讓更多的學習者加入進來!很是感謝! ̄ω ̄=