搞Java開發的,常常會碰到下面兩種異常:html
一、java.lang.OutOfMemoryError: PermGen spacejava
二、java.lang.OutOfMemoryError: Java heap space數組
要詳細解釋這兩種異常,須要簡單重提下Java內存模型。工具
(友情提示:本博文章歡迎轉載,但請註明出處:hankchen,http://www.blogjava.net/hankchen)spa
Java內存模型是描述Java程序中各變量(實例域、靜態域和數組元素)之間的關係,以及在實際計算機系統中將變量存儲到內存和從內存取出變量這樣的低層細節。.net
在Java虛擬機中,內存分爲三個代:新生代(New)、老生代(Old)、永久代(Perm)。線程
(1)新生代New:新建的對象都存放這裏orm
(2)老生代Old:存放重新生代New中遷移過來的生命週期較久的對象。新生代New和老生代Old共同組成了堆內存。htm
(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數組,須要仔細檢查相關代碼。
最後,總結下排查內存故障的方法和技巧有哪些:
一、top命令:Linux命令。能夠查看實時的內存使用狀況。
二、jmap -histo:live [pid],而後分析具體的對象數目和佔用內存大小,從而定位代碼。
三、jmap -dump:live,format=b,file=xxx.xxx [pid],而後利用MAT工具分析是否存在內存泄漏等等。