切換語言為:簡體
堆中 byte[] 陣列佔用了大量記憶體是怎麼回事?OOM故障分析記錄!

堆中 byte[] 陣列佔用了大量記憶體是怎麼回事?OOM故障分析記錄!

  • 爱糖宝
  • 2024-05-15
  • 2128
  • 0
  • 0

現象:

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

分析:

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

嘗試解決問題的步驟:

  1. 加上JVM引數 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath= ,設定當出現OOM時,dump整個堆的資訊

  2. 等OOM後,將檔案複製到電腦上

  3. 用JDK自帶的 visualVM,開啟dump檔案
    設定了最大堆大小 512M,從下圖看出,確實佔滿了導致OOM

  4. 堆中 byte[] 陣列佔用了大量記憶體是怎麼回事?OOM故障分析記錄!

  5. 轉到類例項佔用大小檢視,找到佔用最大的類
    可以看到,總共512M堆大小,byte[]物件佔用了其中的90%,這顯然是異常佔用
    接下來轉到例項檢視,檢視具體的例項
    最大的byte[]物件佔用了 約 10M
    複製byte[]物件中儲存的內容,並在程式碼中構建byte[]物件存入String列印出視覺化內容
    列印出的部分String內容如下,可以看到儲存的是 http header的內容,並且byte[]中99%的內容為0,說明大量空間並未被使用到

  6. 堆中 byte[] 陣列佔用了大量記憶體是怎麼回事?OOM故障分析記錄!

  7. 堆中 byte[] 陣列佔用了大量記憶體是怎麼回事?OOM故障分析記錄!

  8. 堆中 byte[] 陣列佔用了大量記憶體是怎麼回事?OOM故障分析記錄!

HTTP/1.1 200 Access-Control-Allow-Origin: *Access-Contr123

選其中的一個,選擇顯示最近的垃圾回收根節點

堆中 byte[] 陣列佔用了大量記憶體是怎麼回事?OOM故障分析記錄!

看到持有這個byte[]物件的是一個 HeapByteBuffer物件,HeapByteBuffer是java NIO中的物件。

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

於是先去專案中找到如下相關配置:

堆中 byte[] 陣列佔用了大量記憶體是怎麼回事?OOM故障分析記錄!

發現,Tomcat中最大請求頭大小被設定為 10M,和剛纔byte[]物件佔用的大小相似(多出的應為物件頭以及其他多申請的空間,具體要參考原始碼),其次也和前面發現的byte[]物件中儲存的是請求頭資訊的事實相符合,這應該就是問題所在,把這個配置調小點或者乾脆使用預設配置即可。

問題總結:

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

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.