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

現象

工作時遇到某個服務老是頻繁重啓,日誌報錯爲OOM

分析:

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

嘗試解決問題的步驟:

  1. 加上JVM參數  -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=  ,設置當出現OOM時,dump整個堆的信息
  2. 等OOM後,將文件拷貝到電腦上
  3. 用JDK自帶的 visualVM,打開dump文件
  4. 設置了最大堆大小 512M,從下圖看出,確實佔滿了導致OOM
  5. 轉到類實例佔用大小視圖,找到佔用最大的類

        可以看到,總共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[]對象中存儲的是請求頭信息的事實相符合,這應該就是問題所在,把這個配置調小點或者乾脆使用默認配置即可。

問題總結:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章