排查訂單導出內存占用率逐步增大的問題
癥狀
每次導出,導出的內存利用率都會小幅或大幅增長。一次VIP導出後,導出的內存利用率會較大增長。
十次較小導出的結果,從 15:30 有一個小步的內存利用率攀升。
一次VIP大流量導出的結果,從 14:04 有一個大幅的陡峭的攀升。
基本步驟
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.dat scp 到本地
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 工具,解壓安裝即可。
打開 mem.dat
STEP4: 分析內存文件,定位原因和修復問題。
第一輪優化
概覽
Overview: 內存全貌,找到最大內存占用對象;
點擊TopConsumers, 概覽所有占用內存比較較大的對象集合。
點擊 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天內存占用率圖來看,在最後一次發布後,盡管有多次大流量導出,內存占用保持平穩。
參考文章
https://eclipsesource.com/blogs/2013/01/21/10-tips-for-using-the-eclipse-memory-analyzer/
http://www.lightskystreet.com/2015/09/01/mat_usage/
https://blog.csdn.net/wanghuiqi2008/article/details/50724676
https://www.jianshu.com/p/efec4c77e265
https://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-ma/index.html?ca=drs-
https://blog.csdn.net/dashuniuniu/article/details/52224882
排查訂單導出內存占用率逐步增大的問題