2020年03月18日,開年第一個生產事故發送在我的項目組o(╥﹏╥)o
事故發生
在下午2點40時,收到了第一個客戶投訴,緊接着收到了第二個、第三個。
並且不斷的有各業務系統表示自己的系統出現了操作無響應的現象,如此大範圍的影響,推測大概率是網關的功能出現了問題。
事故定位
立馬上日誌平臺搜索Nginx處的請求日誌,果然在Nginx上發現了大量的499響應碼日誌,緊接着拿着top 1請求量的499接口來搜索安全網關的日誌,發現了大量的:java.lang.OutOfMemoryError: GC overhead limit exceeded
,由於怕過年期間生產不穩定,從年前開始,已經很長時間都沒有發佈了,所以排除了新增的代碼BUG。
解決
那麼就有可能是長時間運行慢慢堆積的對象導致的OOM,直接重啓安全網關,重啓後成功解決。
覆盤
java.lang.OutOfMemoryError: GC overhead limit exceeded
根據官方的解釋是在多次gc後效果極差拋出的OOM。
懷疑可能存在內存泄漏,直接分析OOM後的dump文件。
從Dominator Tree圖中排出佔用內存最大的top n對象,挨個查看它的GC root:
再切換到Histogram,同樣排出佔用內存最大的top n對象,挨個查看GC root。
調優
分析後發現skywalking和spring產生了大量的Class對象,skywalking產生了大量的Endpoint對象,spring產生了大量的動態代理對象。並沒有自身代碼產生的內存泄漏對象。
- 適當的調大JVM堆大小
- 進一步調查Endpoint對象,是否可以控制過期時間。
事故的調查期間還發現了服務自啓動以來只有過2次full gc,都是在啓動初期由於Metaspace空間不足擴容導致的full gc,所以還需要調整Metaspace空間。
-XX:MetaspaceSize=128M
另外,我還調低了MaxTenuringThreshold
,這個參數影響進入老年代的年齡閾值。
因爲網關的對象都是請求級別的生命週期,分析gc日誌可以知道young gc平均在30s執行一次,MaxTenuringThreshold
的默認值是15,會導致部分長期存活對象需要將近8分鐘的時間才能進入老年代,本來年輕代的空間就不大,再被這些長期存活對象一佔用,會使得young gc更加頻繁。