排查訂單導出內存佔用率逐步增大的問題

症狀

每次導出,導出的內存利用率都會小幅或大幅增加。一次VIP導出後,導出的內存利用率會較大增加。php

十次較小導出的結果,從 15:30 有一個小步的內存利用率攀升。
html

一次VIP大流量導出的結果,從 14:04 有一個大幅的陡峭的攀升。


數據庫

排查步驟

從網上查資料知,可使用 MAT 工具來排查此類問題。排查基本步驟以下:緩存

STEP1: 運行一次比較大的導出後,使用 jmap 工具從服務器生成內存文件 mem.bin。使用 top -c M 拿到佔用內存最高的 pid;而後服務器

sudo su app
jmap -dump:live,format=b,file=/tmp/mem.bin pid
chmod 777 /tmp/mem.bin

STEP2: 將 mem.bin scp 到本地app

scp shuqin@IP:/tmp/mem.bin /tmp/mem.bin
scp shuqin@IP:/tmp/mem.bin /tmp/mem.bin

STEP3: 在 http://www.eclipse.org/mat/downloads.php 下載 MAT 工具,解壓安裝便可。eclipse

STEP4: 在 MAT 工具打開 mem.bin , 分析內存文件,定位緣由和修復問題。

工具

第一輪優化

概覽

Overview: 內存全貌,找到最大內存佔用對象;
優化

點擊TopConsumers, 概覽全部佔用內存比較較大的對象集合。
.net

點擊 DominatorTree (支配樹) ,能夠看到全部佔用內存比較多的對象的引用鏈


內存佔用明細分析

點擊 Leak Suspects ,查看內存泄露的最大嫌疑。能夠看到 export-execute-thread1 佔用了一個大對象列表 List



線程棧引用


分析和定位緣由

爲何這些對象依然被導出任務線程持有? 緣由極可能是:導出線程是 Context 持有者,Context 在導出結束時沒有清理, 而線程由於某種緣由沒有退出和被回收,致使 Context 依然在內存裏。 作了個 Context 清理以後,再部署並 dump 文件,發現以前的List 大對象已經沒有了。

第二輪優化

清理 Context 以後,從新運行發現內存佔用依然逐增。 從新 dump, 發現貌似 groovy 腳本致使。緣由多是: 在每次導出前,都會把字段配置的groovy腳本加載到針對每次導出的單線程中,而導出結束時這些腳本沒有清理。再作一層腳本引用的清理:

fieldConfig.getCacheScript().setBinding(null);
fieldConfig.setCacheScript(null);

發現再也不有 Groovy 腳本佔用內存的嫌疑。



第三輪優化

第二輪優化後,雖然 groovy 的嫌疑有所減輕,可仍是沒有徹底消除。這下,從代碼上確實看不出什麼了。

原來的策略是每次導出,都從數據庫裏克隆一份報表字段配置,不管是否用到,都建立針對每一個字段的緩存的Script對象,導出結束時清除(可能沒真正清除掉);如今的策略是,作一個Script對象的緩存池。若是沒有Script對象的執行,那麼也不會建立多餘的Script對象佔用空間。從30天內存佔用率圖來看,在最後一次發佈後,儘管有屢次大流量導出,內存佔用保持平穩。



參考文章

10 Tips for using the Eclipse Memory Analyzer

MAT - Memory Analyzer Tool 使用進階

完全搞懂Java內存泄露

使用 Eclipse Memory Analyzer 進行堆轉儲文件分析

構造Dominator Tree以及Dominator Frontier

相關文章
相關標籤/搜索