OutOfMemoryError
問題相信不少朋友都遇到過,相對於常見的業務異常(數組越界、空指針等)來講這類問題是很難定位和解決的。java
本文以最近碰到的一次線上內存溢出的定位、解決問題的方式展開;但願能對碰到相似問題的同窗帶來思路和幫助。git
主要從表現-->排查-->定位-->解決
四個步驟來分析和解決問題。github
<!--more-->數組
最近咱們生產上的一個應用不斷的爆出內存溢出,而且隨着業務量的增加出現的頻次愈來愈高。服務器
該程序的業務邏輯很是簡單,就是從 Kafka 中將數據消費下來而後批量的作持久化操做。運維
而現象則是隨着 Kafka 的消息越多,出現的異常的頻次就越快。因爲當時還有其餘工做因此只能讓運維作重啓,而且監控好堆內存以及 GC 狀況。測試
重啓大法雖好,但是依然不能根本解決問題。
因而咱們想根據運維以前收集到的內存數據、GC 日誌嘗試判斷哪裏出現問題。spa
結果發現老年代的內存使用就算是發生 GC 也一直居高不下,並且隨着時間推移也愈來愈高。3d
結合 jstat 的日誌發現就算是發生了 FGC 老年代也已經回收不了,內存已經到頂。指針
甚至有幾臺應用 FGC 達到了上百次,時間也高的可怕。
這說明應用的內存使用確定是有問題的,有許多賴皮對象始終回收不掉。
因爲生產上的內存 dump 文件很是大,達到了幾十G。也是因爲咱們的內存設置太大有關。
因此致使想使用 MAT 分析須要花費大量時間。
所以咱們便想是否能夠在本地復現,這樣就要好定位的多。
爲了儘快的復現問題,我將本地應用最大堆內存設置爲 150M。
而後在消費 Kafka 那裏 Mock 爲一個 while 循環一直不斷的生成數據。
同時當應用啓動以後利用 VisualVM 連上應用實時監控內存、GC 的使用狀況。
結果跑了 10 幾分鐘內存使用並無什麼問題。根據圖中能夠看出,每產生一次 GC 內存都能有效的回收,因此這樣並無復現問題。
無法復現問題就很難定位了。因而咱們 review 代碼,發現生產的邏輯和咱們用 while 循環 Mock 數據還不太同樣。
查看生產的日誌發現每次從 Kafka 中取出的都是幾百條數據,而咱們 Mock 時每次只能產生一條。
爲了儘量的模擬生產狀況便在服務器上跑着一個生產者程序,一直源源不斷的向 Kafka 中發送數據。
果真不出意外只跑了一分多鐘內存就頂不住了,觀察左圖發現 GC 的頻次很是高,可是內存的回收倒是相形見拙。
同時後臺也開始打印內存溢出了,這樣便復現出問題。
從目前的表現來看就是內存中有許多對象一直存在強引用關係致使得不到回收。
因而便想看看究竟是什麼對象佔用了這麼多的內存,利用 VisualVM 的 HeapDump 功能能夠當即 dump 出當前應用的內存狀況。
結果發現 com.lmax.disruptor.RingBuffer
類型的對象佔用了將近 50% 的內存。
看到這個包天然就想到了 Disruptor
環形隊列。
再次 review 代碼發現:從 Kafka 裏取出的 700 條數據是直接往 Disruptor 裏丟的。
這裏也就能說明爲何第一次模擬數據沒復現問題了。
模擬的時候是一個對象放進隊列裏,而生產的狀況是 700 條數據放進隊列裏。這個數據量是 700 倍的差距。
而 Disruptor 做爲一個環形隊列,再對象沒有被覆蓋以前是一直存在的。
我也作了一個實驗,證實確實如此。
我設置隊列大小爲 8 ,從 0~9 往裏面寫 10 條數據,當寫到 8 的時候就會把以前 0 的位置覆蓋掉,後面的以此類推(相似於 HashMap 的取模定位)。
因此在生產上假設咱們的隊列大小是 1024,那麼隨着系統的運行最終確定會致使 1024 個位置上裝滿了對象,並且每一個位置是 700 個!
因而查看了生產上 Disruptor 的 RingBuffer 配置,結果是:1024*1024
。
這個數量級就很是嚇人了。
爲了驗證是不是這個問題,我在本地將該值換爲 2 ,一個最小值試試。
一樣的 128M 內存,也是經過 Kafka 一直源源不斷的取出數據。經過監控以下:
跑了 20 幾分鐘系統一切正常,每當一次 GC 都能回收大部份內存,最終呈現鋸齒狀。
這樣問題就找到了,不過生產上這個值具體設置多少還得根據業務狀況測試才能知道,但原有的 1024*1024 是絕對不能再使用了。
雖然到了最後也就改了一行代碼(還沒改,直接修改配置),但這排查過程我以爲是有意義的。
也會讓大部分以爲 JVM 這樣的黑盒難如下手的同窗有一個直觀的感覺。
同時也得感嘆 Disruptor 東西雖好,也不能亂用哦!
相關演示代碼查看:
https://github.com/crossoverJie/JCSprout/tree/master/src/main/java/com/crossoverjie/disruptor
你的點贊與轉發是最大的支持。