【問題描述】:服務後端是3節點集羣,凌晨出現2節點磁盤打滿告警並致使業務中斷,定位發現是jvm堆棧日誌hprof,該文件比較大(大概10G+)。恢復線上業務後,恰好另外一節點磁盤沒有滿,並打出堆棧日誌,能夠用來定位這次故障緣由。linux
工具:MemoryAnalyzer-1.8.1.20180910-win32.win32.x86_64後端
tomcat bin/setenv.sh配置堆棧日誌:tomcat
export CATALINA_OPTS="$CATALINA_OPTS -XX:+HeapDumpOnOutOfMemoryError"
export CATALINA_OPTS="$CATALINA_OPTS -XX:HeapDumpPath=/opt/admin/logs/CloudNetmonitor-Computer"安全
【定位過程】:服務器
第一步 從線上導出堆棧日誌到本地分析網絡
因爲公司安全策略,線上與本地網絡隔離,須要堡壘機傳送文件而且限制大小,在linux服務器上須要拆分日誌文件:jvm
拆包: tar czf - sourcefile | split -b 90m - dest_split.工具
合併:cat dest_split.* >> dest.tar.gz線程
解包:tar zxvf dest.tar.gz日誌
第二步 啓動MAT工具
啓動前配置MemoryAnalyzer文件,調大內存 -Xmx12g
MAT工具 File- Open Head Dump 導入hprof文件,大文件可能持續一段時間,導入後
第三步 分析內存泄漏
首先根據 餅狀圖能夠直觀的發現,有內存泄漏9.7G,點擊 leak suspecks 能夠查看可疑泄漏位置,點擊查看詳細,可發現是RedisListenerPool這個類中定義的 固定線程池引用變量堆內存過大。
MAT工具提供的分析仍是比較全面,查看「Accumulated Objects in Dominator Tree」,發現是線程池隊列長度打滿,並且該隊列是個鏈表阻塞隊列,轉到對應代碼位置
問題代碼位置:
private ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
查看固定線程池源碼:
找到問題了,這裏默認定義的是不指定容量大小的阻塞隊列,意味着是無限大小。做爲開發者,咱們須要注意的是,若是構造一個LinkedBlockingQueue對象,而沒有指定其容量大小,LinkedBlockingQueue會默認一個相似無限大小的容量(Integer.MAX_VALUE),這樣的話,若是生產者的速度一旦大於消費者的速度,也許尚未等到隊列滿阻塞產生,系統內存就有可能已被消耗殆盡了。