問題
背景:服務異常,交易無法發到服務器,經排查發現大量SYN_RECV,重啓服務後交易正常。有大量CLOSE_WAIT、SYN_RECV連接,交易日誌無增量輸出,每次重啓後不久服務又出現無法訪問現象,反覆重啓3次後,截止發郵件時生產上仍有CLOSE_WAIT,CLOSE_WAIT、SYN_RECV許多連接爲F5地址,懷疑可能由F5引起大量非正常連接,進而導致交易無法正常使用。console.log中出現java.lang.OutOfMemoryError
問題排查及分析
- 收集相關信息。確認異常,明確現象:
- 大量close_wait:根據TCP三次握手建立通訊可以知道,這個一般是在服務端出現的。排除惡意攻擊的情況,只會在客戶端斷開鏈接之後服務端未及時關閉鏈接時出現;具體流程見相關文章
- 內存溢出:java.lang.OutOfMemoryError: Java heap space ;該異常爲內存不足,一般是程序問題導致的。
- 在JVM參數中新增下列參數,在JVM虛擬機發生內存溢出異常時,自動產生DUMP文件
-
#當應用拋出 OutOfMemoryError 時自動生成dump文件
-XX:+HeapDumpOnOutOfMemoryError
#dump文件輸出路徑
-XX:HeapDumpPath=/home/sunefrs/sunefrs.dump
- 從dump文件上看,Log4j佔用了大量內存.其回根收節點見下圖
- 分析當前可能引起異常現象的可能原因:
- 生產日誌量很大,大部分爲圖片比對交易,交易報文一般在幾百K到1、2M不等,暫未發現超大報文的情況。
- 有大量F5調用異常。
- dump文件顯示:有大量內存被log4j佔用了。很可能是因爲日誌打印問題。
- 確認可能原因是否成立:
- F5正常。不太可能是F5造成的
- 日誌量很大,從dump文件分析有極大可能是由於log4j打印日誌造成的
- 復現生產問題步驟:
- 使用jmeter進行壓測。使用大報文模擬交易。
- 打開java visualVm 監控內存使用情況
- 打開console.log監控控制檯日誌
- 使用netstat命令監控close_wait情況【此處不放截圖,僅說結論】:
- 60併發壓測:屏蔽日誌打印,垃圾回收正常,交易未出現問題
- 60併發壓測:不屏蔽日誌打印,垃圾無法及時回收,交易OOM,並出現大量close_wait【之前測試沒出現是因爲設置了Keep-Alive參數
- 問題原因分析:
- 從業務日誌上看,交易報文比較大,在日誌打印的時候會把字段打印出來如下:日誌量將會是8倍,如果是2M圖片,會寫16M以上日誌【這裏取值有點問題,正常生成是會寫8倍日誌的】【補充說明:實際上還有16進制的日誌打印也會有2次,比原圖片還大的多圖片未寫出來】
- 再看log4j配置文件:日誌是異步打印的。當交易量較大的時候。日誌打印量會超級大,假設每次報文圖片大小爲2M,有每分鐘20個請求,每分鐘就會有320M以上的日誌量。且目前生產log4j配置爲兩處輸出,因此日誌量*2也就是640M以上,加上其他交易的日誌,日誌量會非常大。
【測試環境寫磁盤速度大約爲87.2MB/sec,考慮到還有日誌壓縮和其他系統的讀寫,程序寫日誌不會把寫磁盤速度用滿。因此會有大量日誌停留在內存中。因此會造成內存溢出。內存溢出後程序直接卡死,不能正常處理交易且無法恢復】
- 驗證分析:
- 單線程且間隔較大時間調用交易:交易正常內存正常回收
- 高併發調用交易:程序快速內存溢出
- 關閉日誌:高併發內存快速釋放,交易正常
總結
內存溢出原因爲:日誌打印量過大,佔用了過多磁盤資源,寫日誌的速度跟不上新增待寫日誌速度導致。
查內存溢出最重要的就是要找到是什麼類佔用了內存,爲什麼不能正常釋放。明確了這兩點。內存溢出問題就不難解決。
解決方案
- 推薦方案:使用log4j過濾器,屏蔽圖片字段相關打印。移除多餘的日誌打
- 可選方案:分多個jvm處理類似大報文的交易,把這個jvm的log4j文件直接屏蔽日誌。
- 可選方案:修改底層代碼,調整日誌打印方式。