1. 程式人生 > >排查訂單導出內存占用率逐步增大的問題

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

加載 source nat using lis pid works ont lips

癥狀

每次導出,導出的內存利用率都會小幅或大幅增長。一次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

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