【經驗分享】OOM故障分析記錄:byte[] 占用了大量內存


現象:

工作時遇到某個服務老是頻繁重啟,日志報錯為OOM

分析:

出現OOM是因為整個堆內存不夠用了,此時JVM首先嘗試擴展更多的空間,其次GC嘗試回收內存,前兩種方法無果的情況下只能報OOM並退出
可能的情況:內存不夠、內存泄漏

嘗試解決問題的步驟:

  1. 加上JVM參數 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath= ,設置當出現OOM時,dump整個堆的信息
  2. 等OOM后,將文件拷貝到電腦上
  3. 用JDK自帶的 visualVM,打開dump文件
    設置了最大堆大小 512M,從下圖看出,確實占滿了導致OOM
    20191122170147350.png
  4. 轉到類實例占用大小視圖,找到占用最大的類
    image.png
    可以看到,總共512M堆大小,byte[]對象占用了其中的90%,這顯然是異常占用
    接下來轉到實例視圖,查看具體的實例
    image.png
    最大的byte[]對象占用了 約 10M
    拷貝byte[]對象中存儲的內容,並在代碼中構建byte[]對象存入String打印出可視化內容
    image.png
    打印出的部分String內容如下,可以看到存儲的是 http header的內容,並且byte[]中99%的內容為0,說明大量空間並未被使用到
HTTP/1.1 200 
Access-Control-Allow-Origin: *
Access-Contr

選其中的一個,選擇顯示最近的垃圾回收根節點
image.png
看到持有這個byte[]對象的是一個 HeapByteBuffer對象,HeapByteBuffer是java NIO中的對象。

程序中沒有使用NIO,推測NIO應該在Tomcat中被使用,並且Tomcat的默認配置不可能為 10M這么不合理的值,那感覺可能是有不合理的自定義配置存在。

於是先去項目中找到如下相關配置:
image.png

發現,Tomcat中最大請求頭大小被設置為 10M,和剛才byte[]對象占用的大小相似(多出的應為對象頭以及其他多申請的空間,具體要參考源碼),其次也和前面發現的byte[]對象中存儲的是請求頭信息的事實相符合,這應該就是問題所在,把這個配置調小點或者干脆使用默認配置即可。

問題總結:

咨詢了相關同事,為了傳輸較大的文件,調大了 tomcat max-http-post-size,順手改了 max-http-header-size,容器初始化處理請求的線程池時,每個線程都會申請 此處為 10M大小的byte[]對象,並且請求處理線程的生命周期一般和服務的生命周期一致,也就是說,線程持有的 byte[]對象在整個服務周期中是一直存活的。一般線程池的規模少說也在幾十個,也意味着服務正常工作時,幾百兆的堆內存(也可能是堆外內存,具體看Tomcat配置使用哪個)會被請求處理線程一直占用,當分配的內存較少時,很快OOM

轉載:一次OOM故障分析記錄:HeapByteBuffer byte[] 占用了大量內存


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM