前一篇介紹了線上應用程式中斷排查之一:高CPU佔用,這篇主要分析高記憶體佔用故障的排查。
搞Java開發的,經常會碰到下面兩種異常:
1、java.lang.OutOfMemoryError: PermGen space
2、java.lang.OutOfMemoryError: Java heap space
要詳細解釋這兩種異常,需要簡單重提下Java記憶體模型。
(友情提示:本博文章歡迎轉載,但請註明出處:hankchen,http://www.blogjava.net/hankchen)
Java記憶體模型是描述Java程式中各變數(執行個體域、靜態域和數組元素)之間的關係,以及在實際電腦系統中將變數儲存到記憶體和從記憶體取出變數這樣的低層細節。
在Java虛擬機器中,記憶體分為三個代:新生代(New)、老生代(Old)、永久代(Perm)。
(1)新生代New:建立的對象都存放這裡
(2)老生代Old:存放從新生代New中遷移過來的生命週期較久的對象。新生代New和老生代Old共同組成了堆記憶體。
(3)永久代Perm:是非堆記憶體的組成部分。主要存放載入的Class類級對象如class本身,method,field等等。
如果出現java.lang.OutOfMemoryError: Java heap space異常,說明Java虛擬機器的堆記憶體不夠。原因有二:
(1)Java虛擬機器的堆記憶體設定不夠,可以通過參數-Xms、-Xmx來調整。
(2)代碼中建立了大量大對象,並且長時間不能被垃圾收集器收集(存在被引用)。
如果出現java.lang.OutOfMemoryError: PermGen space,說明是Java虛擬機器對永久代Perm記憶體設定不夠。
一般出現這種情況,都是程式啟動需要載入大量的第三方jar包。例如:在一個Tomcat下部署了太多的應用。
從代碼的角度,軟體開發人員主要關注java.lang.OutOfMemoryError: Java heap space異常,減少不必要的對象建立,同時避免記憶體流失。
現在以一個實際的例子分析記憶體佔用的故障排查。
通過top命令,發現PID為9004的Java進程一直佔用比較高的記憶體不釋放(24.7%),出現高記憶體佔用的故障。
想起上一篇線上應用程式中斷排查之一:高CPU佔用介紹的PS命令,能否找到具體是哪個的線程呢。
ps -mp 9004 -o THREAD,tid,time,rss,size,%mem
遺憾的是,發現PS命令可以查到具體進程的CPU佔用情況,但是不能查到一個進程下具體線程的記憶體佔用情況。
只好尋求其他方法了,幸好Java提供了一個很好的記憶體監控工具:jmap命令
jmap命令有下面幾種常用的用法:
•jmap [pid]
•jmap -histo:live [pid] >a.log
•jmap -dump:live,format=b,file=xxx.xxx [pid]
用得最多是後面兩個。其中,jmap -histo:live [pid] 可以查看當前Java進程建立的活躍對象數目和佔用記憶體大小。
jmap -dump:live,format=b,file=xxx.xxx [pid] 則可以將當前Java進程的記憶體佔用情況匯出來,方便用專門的記憶體分析工具(例如:MAT)來分析。
這個命令對於分析是否有記憶體流失很有協助。具體怎麼使用可以查看本博的另一篇文章:利用Eclipse Memory Analyzer Tool(MAT)分析記憶體流失
這裡詳細介紹下jmap -histo:live [pid] 命令:
從上圖可以看出,int數組、constMethodKlass、methodKlass、constantPoolKlass都佔用了大量的記憶體。
特別是佔用了大量記憶體的int數組,需要仔細檢查相關代碼。
最後,總結下排查記憶體故障的方法和技巧有哪些:
1、top命令:Linux命令。可以查看即時的記憶體使用量情況。
2、jmap -histo:live [pid],然後分析具體的對象數目和佔用記憶體大小,從而定位代碼。
3、jmap -dump:live,format=b,file=xxx.xxx [pid],然後利用MAT工具分析是否存在記憶體流失等等。
(友情提示:本博文章歡迎轉載,但請註明出處:hankchen,http://www.blogjava.net/hankchen)