測試同學在管理後臺上傳一張3M的圖片時,發現介面停留在卡死的狀態,就喊我排查下。聽到這個後首先要做的就是看這個管理後臺服務的日誌。
從日誌裡清楚地看到,居然是可惡的堆記憶體溢位。
堆記憶體溢位的排查過程
還好我們jvm的啟動引數裡有配置在發生OOM時dump記憶體快照
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/app/sc-manage/logs
我登入到OOM的這臺機器把記憶體快照檔案下載到本地,準備用MAT來分析下。
MAT是什麼
MAT是一款強大的記憶體分析工具,如果你的開發工具是Eclipse,可以直接整合MAT外掛來使用,很多人其實開發是用IntelliJ IDEA的,所以這個時候可以直接下載一個MAT來使用即可,官網有下載地址可以百度。
大家選擇自己的膝上型電腦的作業系統對應的版本就可以了,他是支援Windows、Mac、Linux三種作業系統的。
下載好MAT後,在他的安裝目錄裡,可以看到一個檔名字叫做:MemoryAnalyzer。ini
這個檔案裡的內容類似如下所示:
如果dump出來的記憶體快照很大,比如有幾個G,你務必在啟動MAT之前,先在這個配置檔案裡給MAT本身設定一下堆記憶體大小,比如設定為4個G,或者8個G,他這裡預設-Xmx1024m是1G。接著大家直接啟動MAT即可,啟動之後看到的介面中有一個選型是:Open a Heap Dump,就是開啟一個記憶體快照的意思,選擇他,然後選擇本地的一個記憶體快照檔案即可。
下面是我開啟檔案後看到的截圖
從這裡可以看到主要是這兩個200多M的大物件,但是這裡看不出是程式碼哪裡建立的這些大物件的,我們點選stackTrace找到有問題的業務程式碼
就是我們自己封裝的上床圖片元件有問題,將進度條拖到右邊找到出問題的程式碼在哪一行
然後開啟程式碼找到是這一行程式碼導致的
這一行程式碼是用的開源工具主要用來壓縮圖片的,上面分析的大物件就是這個壓縮工具核心程式碼裡生成的。
解決辦法
我看了下我們JVM的啟動引數配置,如下所示
堆記憶體設定非常不合理,年輕代256M老年代是768M,然後eden和survivor區的比例是8:1:1,這就導致每個S區大小隻有25M左右,導致壓縮時大物件在S區放不下直接進入老年代,老年代也裝不下了觸發Full gc又不能回收掉就導致OOM了,公司目前的堆記憶體分配如下
在瞭解到公司機器配置是8核32G的,雖然部署了十幾個服務,每個服務的JVM引數都是用的同一套模板,機器剩餘還有18G記憶體,我這邊修改了管理後臺服務JVM啟動引數重啟了例項,修改後的JVM引數如下
修改後的堆記憶體配置如下
修改後的堆記憶體配置如下
目的是讓壓縮產生的200M大物件比S區的50%還要小,不會觸發動態年齡判斷機制等直接進入老年代,讓這些物件在young gc裡就能回收掉不要進入老年代。重啟例項後再管理後臺上傳大圖片時,用jstat看了下gc情況,基本達到預期了。
寫在後面
壓縮圖片一般業內用的比較多是ImageMagic,我們系統是前人留下的一個工具,從結果來看壓縮時這麼耗費記憶體,並不是那麼好用,後續考慮切換到ImageMagic,現在先暫時擴大JVM的堆記憶體來解決。