JVM調試和優化(一)
概述
事情發生的過程是這樣的
有一天突然收到亞馬遜amazon的郵件說我的web服務cpu over max , memory 也over max 了 ,這一刻,我的內心是崩潰的 , 心里設想了一萬種意外和不可能是程序問題的理由 . 事實是太天真了,不是程序問題才怪 . 臨時處理方式是重啟服務 ,畢竟內存和cpu使用率爆棚還是需要很長時間的 , 這段時間可以排查到底是因為什么 .
一些可能導致OOM即內存溢出的原因
- 堆內存設置太小
- 永久代設置太小(java8已不存在這個問題,因為metaspace已經替代了perm)
- 代碼中創建了很多大對象 , 且一直因為被引用不能被回收
- 長生命周期對象持有短生命周期對象的引用
- 靜態集合類引起的內存泄漏 , 例如HashMap和Vector等
因為他們是靜態的, 他們的生命周期與應用一致 , 所以他們引用的對象不會被釋放 , 所以需要特別注意自己是否有靜態集合存了許多對象的情況 - 單例模式
單例對象初始化后再JVM整個生命周期中存在 , 如果單例對象持有對外部對象的引用那么整個對象不會被JVM回收 - 全局集合
- 類加載器
逐個分析和排除
1和2設置過小的問題
逐個比較好解決 , 合理增大設置或增大硬件物理內存即可 ,這里可能要說的是metaspace如果不設置會有一個默認值 , 會很小 , 但是它會在不夠用的時候自動增大 ,直逼最大物理內存 .(此變更可以某種程度是可以避免OOM)具體請百度或Google本人不再贅述 .
3 代碼中創建了很多大對象 , 且一直因為被引用不能被回收
第一步,使用jmap -heap pid查看堆使用情況
第二步, 使用jmap -histo:live pid 查看實例數和實例所占內存大小
第三步, 統計所有實例所占內存大小
jmap -histo:live 17863|awk ‘{if(NR>3)a+=$3}END{print a}’
233455600
這個單位是byte , 換算后222.64061MB
大對象倒是沒有 , 但是[C代表字符的數組占用了大多數的內存空間 , 雖然現在講示例時它占得少 ,實際當內存接近90%時 ,字符數組還是在第一位 .([B表示布爾數組]) , 結論是不存在特別多的大對象占用了內存空間 , 字符數組雖然也占了很多內存但不是導致內存溢出的主要原因.