記錄一次線上OOM狀況排查過程

現象 

某天凌晨,手機忽然告警,線上某臺機子內存使用率超過90%,當時覺得是有定時任務在跑,再加上夜已深了,沒有去排查具體緣由。次日早上有發佈,內存降下來了,白天就沒有去調查這個問題。等到傍晚高峯期的時候,又接到內存調用超過90%的告警,而且持續一段時間後,線上的某臺機子掛了。

現場保存 

當時一臺機子掛掉後,立刻重啓了掛掉的機子,而且把另外一臺機子的內存信息dump下來。
java

jmap -dump:format=b,file=文件名 [pid] 
服務器

 在dump過程當中遇到小插曲,沒法dump下來。jvm


這種狀況是由於非當前線程用戶致使,在命令前面加上sudo -u 用戶便可
工具

同時保存了線程信息 測試

sudo -u jetty jstack pid > /tmp/jstack2018-04-18.txt
字體

問題定位

查看服務器系統日誌

cat /var/log/messagesspa


上圖看到 Killed process 7364, UID 502, (java) total-vm:10511252kB, anon-rss:7489308kB  java內存使用了7G多。ps:上一行的java進程的2627813 和 1872416都是頁數,每頁4K。.net

能夠發現容器內存使用由於超過系統內存被kill掉了。線程

使用mat分析具體內存泄漏問題。

因爲本人對mat工具使用的還不熟練,看到分佈圖後,total一共才103.6MB,覺得堆內內存沒有問題,便往堆外內存溢出方向去考慮。陸陸續續的檢查了代碼中的ThreadLocal的使用,對象也有及時清理,檢查了線上線程的數量,都沒有發現明顯問題。3d

轉載一篇線程過多致使的堆外內存溢出文章:線程數過多致使堆外內存溢出

繼續使用mat分析,使用Leak Suspects功能,發現內存中有大量的TrueTypeFont對象

正好想到在該應用中,有使用到字體畫圖,發送圖片的功能。因而乎查詢一下OOM當時請求狀況,發現確實有大量的畫圖的請求,而且發現某個請求中同時畫600多張畫。

問題重現

接着,在公司的測試環境下模擬了下請求,果真內存使用率由60多一會兒升到了80多。

回頭檢查了一下畫圖的代碼,發如今處理字體的地方確實有問題。

該方法的邏輯爲加載服務器上的某個字體文件,使用畫筆畫一張二維碼,而後保存到服務器的臨時目錄下。

這裏在加載字體的時候沒作好處理,致使每次過來一個畫的任務就會加載一次字體文件,內存中建立一個字體對象,而咱們服務器上的字體文件大小約有16M,也就至關於每次畫一幅畫就須要加載16M的內存大小。

分析緣由 

因爲當時對mat不熟悉,還在懷疑是否是Font操做了堆外空間致使的。因而乎準備測試一下堆外空間。

排除堆外空間溢出可能性

咱們服務器的配置爲8G內存,jvm配置爲 -Xmx4428m -Xms4428m -Xmn2767m,堆外空間若是不限制的話,會和jvm使用差很少。這時候,咱們限定堆外空間大小,好比咱們指定堆外空間好比說只有100M -XX:MaxDirectMemorySize=100m 。

若是說Font使用的是堆外空間,那麼堆外空間就會很快到達100M,而且進行Full GC,阻塞全部請求,Stop The World。這時候,只要Full GC清理及時,後面阻塞請求繼續進來,繼續滿100M,繼續Full GC。這樣,內存使用率永遠也不會到8G,就不會出現被系統kill掉的狀況了。

可是在測試過程當中,發現內存使用率仍是在一路飆升,最終仍是被kill了。因此後來只能再次懷疑是堆內內存溢出。用一樣的策略,將堆內內存設置爲1G(100M應用起不來了),發現內存沒有繼續往上升,而且查看了下gc日誌,一直在進行Fulll GC,從這大體能夠看出使用的是堆內的內存。

由此能夠看出,當時高峯期,大量畫圖請求進來,致使大量的大對象加載進內存,最終致使jvm內存超過了系統內存,容器被系統kill掉。

解決問題

將Font改成單例模式,內存中只存在一份。


在公司測試環境再次試了下,內存使用率沒有變化。發佈上線後,線上該問題沒有再復現,問題暫時告一段落。

總結

  • 對於線上故障首先要保護現場。
  • 對於大對象操做要特別當心。
  • 經常使用工具要熟練掌握,不然就會浪費不少沒必要要的時間。使用mat去分析問題的時候,更應該關心對象比例,而不是大小。若是熟悉工具更多一點,能夠省去後面堆外空間的排查工做。
  • 有告警要及時看,幸虧本次只掛掉了一臺機子,對線上沒有太大影響。

後續問題

後續調研發現,當請求量小的時候,內存上去後沒有OOM,可是內存一直沒有往降低,懷疑出現了內存泄漏。使用mat的Histogram搜索TrueTypeFont


再查看他的根回收節點。


過濾掉弱引用等對回收沒有影響的引用。


發現主要有這麼幾個類,咱們看最多的類Disposer。上網查了下Disposer的做用。


在mat中,我已通過濾掉了弱引用,剩下的發現有個FontStrikeDisposer對TrueTypeFont爲強引用。可是這些數量比較少,只有8個,應該起不到做用纔對。到此,頭緒就斷了,回頭擼擼TrueTypeFont的源碼看能不能有什麼發現。

相關文章
相關標籤/搜索