線上現象(各類監控數據)
1.公司項目在監控平臺上開始報警(jvm堆內存佔用報警,FullGC次數超頻率報警) spring
2.觀察具體的監控圖標(預發機器) 線程數平穩(260左右)
3. 方法監控能夠看到在fullGC比較頻繁時,業務方法幾乎無響應
線上配置(jvm配置,運行時內存分佈)
- 項目版本:jdk8 ,spring 5, 默認垃圾處理器 Parallel GC with 43 thread(s) -Xms800m -Xmx800m -XX:MaxPermSize=256m
2.運行時jstat
3.運行時jmap histo
4.heap
5.dump文件1G左右,不發了,稍後看一下MAT分析的圖表吧
邏輯分析(定位問題大體方向)
1.經過監控和運行時數據分析,堆內存(年輕代和老年代)、非堆(方法區)、均打滿配置內存 2.即便FullGC,堆內存和非堆也只能回收少量內存,而且總體水位傾斜向上,直到內存溢出jvm
經過邏輯分析,內存溢出問題來自於存在泄漏,接下來分析dump文件工具
內存分析(定位問題確切泄漏源)
採用MAT工具載入dump文件進行leak分析 性能
經過分析能夠看出紅色的業務方法保留引用太多, 找到泄漏源了,接下來就分析業務代碼具體的問題
代碼分析(定位致使泄露代碼片斷)
經過分析,咱們最終定位了這段代碼,咱們改造過程當中引入了一個開源的屬性運行時拷貝的包 可是咱們每次轉換的時候都會先register一遍轉換代理類,而此類底層爲每次register註冊一個新生的代理類被加載到非堆 可是又被業務代碼中的MAPPER_FACTORY引用,致使每次生成的實例充斥着年輕代又到老年代
最終的現象就是老年代、年輕代、非堆內存同時爆滿,而又GC不掉,內存泄露直到溢出線程
代碼處理
找到了具體的代碼問題,咱們將同一個類轉換的register在系統啓動時注入一次就行,不用每次調用註冊,這樣的話就不會頻繁建立和加載,就能夠解決上述問題3d
本地驗證
本地驗證錯誤使用代碼,能夠復現問題 代理
加入 參數-verbose -verbose:gc 也 能夠看到新增的代理類在循環中瘋狂生成與加載, 修復後本地監控數據平穩運行,具體圖標參考預發驗證圖表
預發驗證
預發驗證後續咱們還將採用壓測排除性能與其餘內存問題,這次排除結束。
後記
這次問題仍是屬於比較常見的內存溢出分析,總體按着經常使用流程沒有太多的難點,只有在分析register的時候一時定位不到是開源包的register方法調用的緣由(雖然過後感受很簡單,當時也是耗費了30分鐘左右才發現)。cdn
troube shooting 三要素: 鍛鍊本身的邏輯思惟、鍛鍊本身的技術能力、多看多查blog