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