強如 Disruptor 也發生記憶體溢出?

来源:https://www.cnblogs.com/crossoverJie/archive/2018/08/29/9552119.html
-Advertisement-
Play Games

OutOfMemoryError 問題相信很多朋友都遇到過,相對於常見的業務異常(數組越界、空指針等)來說這類問題是很難定位和解決的。 本文以最近碰到的一次線上記憶體溢出的定位、解決問題的方式展開;希望能對碰到類似問題的同學帶來思路和幫助。 ...


前言

OutOfMemoryError 問題相信很多朋友都遇到過,相對於常見的業務異常(數組越界、空指針等)來說這類問題是很難定位和解決的。

本文以最近碰到的一次線上記憶體溢出的定位、解決問題的方式展開;希望能對碰到類似問題的同學帶來思路和幫助。

主要從表現-->排查-->定位-->解決 四個步驟來分析和解決問題。

表象

最近我們生產上的一個應用不斷的爆出記憶體溢出,並且隨著業務量的增長出現的頻次越來越高。

該程式的業務邏輯非常簡單,就是從 Kafka 中將數據消費下來然後批量的做持久化操作。

而現象則是隨著 Kafka 的消息越多,出現的異常的頻次就越快。由於當時還有其他工作所以只能讓運維做重啟,並且監控好堆記憶體以及 GC 情況。

重啟大法雖好,可是依然不能根本解決問題。

排查

於是我們想根據運維之前收集到的記憶體數據、GC 日誌嘗試判斷哪裡出現問題。

結果發現老年代的記憶體使用就算是發生 GC 也一直居高不下,而且隨著時間推移也越來越高。

結合 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

你的點贊與轉發是最大的支持。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • "上一節" 我們已經分析了vue.js是通過Object.defineProperty以及發佈訂閱模式來進行數據劫持和監聽,並且實現了一個簡單的demo。今天,我們就基於上一節的代碼,來實現一個MVVM類,將其與html結合在一起,並且實現v model以及{{}}語法。 tips:本節新增代碼(去 ...
  • 本地調試如過出現請求失敗請將 微信開發者工具 > 詳情(右上角) > 不校驗合法功能變數名稱、web-view(業務功能變數名稱)、TLS 版本以及 HTTPS 證書 勾上即可 微信支付小程式 C#後端 ...
  • react 跨域訪問後臺,預設是有跨域問題,並且火弧和谷歌瀏覽器,對跨域問題展示還不一樣. 谷歌瀏覽器如下圖: 此處狀態是200,然而在Response卻沒有任何信息,如下圖 然而火弧瀏覽器,對該問題的描述,就清淅得多, 火弧瀏覽器告訴我們,跨域了,關於react跨域的帖子,網上也有相關帖子,搜索到 ...
  • 獲取瀏覽器視窗的可視區域高度和寬度,滾動條高度。 jquery函數獲取方法 ...
  • 很多時候,我們在操作數組的時候往往就是一個for迴圈乾到底,對數組提供的其它方法視而不見。看完本文,希望你可以換種思路處理數組,然後可以寫出更加漂亮、簡潔、函數式的代碼。 reduce 數組裡所有值的和 將二維數組轉化為一維數組 計算數組中每個元素出現的次數 使用擴展運算符和initialValue ...
  • 最近一些人在介紹方案時,經常會出現redis這個詞,於是很多小伙伴百度完redis也就覺得它是一個緩存,然後項目裡面把數據丟進去完事,甚至有例如將實體屬性拆分塞進redis hash裡面的奇怪用法等等!原因是什麼呢?大家覺得redis火,使用了redis項目就是高大上的,於是不管三七二十一,項目里用 ...
  • 集群 一組服務實體協同工作,提供比單個服務實體更可伸縮和可用的服務平臺。在客戶端中,集群就像一個服務實體,但事實上,集群是由一組服務實體組成的。 與單個服務實體(服務實體即伺服器)相比,集群提供了以下兩個關鍵特性: 可伸縮性(彈性變化):新的服務實體可以動態地添加到集群中,從而提高集群的性能。 高可 ...
  • 在看這篇文章之前,本人建議你先看看其他的關於這個問題的本質以及解決的方案。 我們做開發,通過SVN同步項目,連接的是遠程資料庫,同學在SVN導出的項目在Windows上可以運行,我在Windows上也可以運行,但是在Linux上就是不能運行--意思就是說什麼賬號,密碼,埠,許可權問題全都沒有。 但是 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...