現象:
工作時遇到某個服務老是頻繁重啟,日志報錯為OOM
分析:
出現OOM是因為整個堆內存不夠用了,此時JVM首先嘗試擴展更多的空間,其次GC嘗試回收內存,前兩種方法無果的情況下只能報OOM並退出
可能的情況:內存不夠、內存泄漏
嘗試解決問題的步驟:
- 加上JVM參數 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath= ,設置當出現OOM時,dump整個堆的信息
- 等OOM后,將文件拷貝到電腦上
- 用JDK自帶的 visualVM,打開dump文件
設置了最大堆大小 512M,從下圖看出,確實占滿了導致OOM
- 轉到類實例占用大小視圖,找到占用最大的類
可以看到,總共512M堆大小,byte[]對象占用了其中的90%,這顯然是異常占用
接下來轉到實例視圖,查看具體的實例
最大的byte[]對象占用了 約 10M
拷貝byte[]對象中存儲的內容,並在代碼中構建byte[]對象存入String打印出可視化內容
打印出的部分String內容如下,可以看到存儲的是 http header的內容,並且byte[]中99%的內容為0,說明大量空間並未被使用到
HTTP/1.1 200
Access-Control-Allow-Origin: *
Access-Contr
選其中的一個,選擇顯示最近的垃圾回收根節點
看到持有這個byte[]對象的是一個 HeapByteBuffer對象,HeapByteBuffer是java NIO中的對象。
程序中沒有使用NIO,推測NIO應該在Tomcat中被使用,並且Tomcat的默認配置不可能為 10M這么不合理的值,那感覺可能是有不合理的自定義配置存在。
於是先去項目中找到如下相關配置:
發現,Tomcat中最大請求頭大小被設置為 10M,和剛才byte[]對象占用的大小相似(多出的應為對象頭以及其他多申請的空間,具體要參考源碼),其次也和前面發現的byte[]對象中存儲的是請求頭信息的事實相符合,這應該就是問題所在,把這個配置調小點或者干脆使用默認配置即可。