Node怎麼排查記憶體洩漏?以下這篇文章就來給大家整理總結一下Node記憶體洩漏排查經驗,希望對大家有幫助!
在 Nodejs
服務端開發的場景中,記憶體洩漏
絕對是最令人頭痛的問題;
但只要專案一直在開發迭代,那麼出現 記憶體洩漏
的問題絕對不可避免,只是出現的時間早晚而已。所以系統性掌握有效的 記憶體洩漏
排查方法是一名Nodejs
工程師最基礎、最核心的能力。
記憶體洩漏處理的困難是如何能在無數的功能、函數中找到具體是哪一個功能中的哪一個函數的第多少行到多少行引起了記憶體洩漏。
很遺憾目前市面上沒有能夠輕鬆定位記憶體洩漏的工具,所以許多初次遇到這種問題的工程師會感到茫然,一下子不知道該如何處理。
這裡我以22年的一次排查 記憶體洩漏
的案例分享一下我的處理想法。
問題描述
2022 Q4
某天,研發用戶群中回饋我們的研發平台不能訪問,後台中出現了大量的異常任務未完成。
第一反應就是可能出現了記憶體洩漏還好服務接入了監控(prometheus
grafana
),在grafana
監控面板中發現在10.00 後記憶體一直在漲沒有下來過出現了明顯的資料外洩。 【相關教學推薦:nodejs影片教學】
說明:
#process memory
:rss
(Resident Set Size),進程的常駐記憶體大小。heapTotal
: V8 堆的總大小。heapUsed
: V8 堆已使用的大小。external
: V8 堆外的記憶體使用量。在
Nodejs
中可以呼叫全域方法process.memoryUsage()
取得這些資料其中heapTotal
和heapUsed
是V8 堆的使用情況,V8 堆是Node.js
中JavaScript 物件儲存的地方。而external
則表示非 V8 堆中分配的內存,例如 C 物件。rss
則是進程所有記憶體的使用量。一般看監控資料的時候重點關注heapUsed
的指標就行了
#記憶體洩漏類型
##記憶體洩漏主要分為:- 全域性洩漏
- 局部性洩漏
#其實不管是全域性記憶體洩漏還是局部性的記憶體洩漏,要做的都是盡可能縮小排除範圍。
全域性記憶體洩漏
全域性內容洩漏出現一般高發於:中間件與
元件中,這種類型的記憶體洩漏排查起來也是最簡單的。
2022 Q4 中遇到的記憶體洩漏不屬於這個類型,所以還得按照局部性洩漏的思路進行分析。
二分法排查
這種類型我就不講其它科學的分析方法了,這種情況下我認為使用二分法排查是最快的。流程流程:
- 先註解一半的程式碼(減少一半
中間件
、
元件、或其它公用邏輯的使用)
- 隨便選擇一個介面或新寫一個測試介面進行壓測
- 如果出現記憶體洩漏,那麼洩漏點就在目前使用的程式碼之中,若沒有洩漏則洩漏點出現在
- 然後一直循環往復上述流程大約20 ~ 60 min 一定可以定位到記憶體洩漏的詳細位置
2020 年的時候我在做基於##局部性記憶體洩漏排查Nuxt
SSR 應用時,上線前壓測發現應用記憶體洩漏,判斷定為全局性的洩漏之後,採用二分法排查大約花了30min 就成功定位了問題。
當時洩漏的原因是我們在服務端使用
axios導致的洩漏,後來統一
axios相關的全換成
node-fetch後就解決了,從此換上了
axios PDST後來絕對不會在
Node服務中使用
axios了
大多數記憶體洩漏的情況都是局部性的洩漏,洩漏點可能存在與某個
中間件、某個介面
、某個非同步任務
中,由於這樣的特性它的排查難度也較大。這種情況都會做 heapdump
來分析。 <p>這裡主要講我這個案例中的思路關於<code>heapdump
的詳細說明我放在下個段落,
Heap Dump
:堆轉儲, 後面部分都使用heapdump
表示,做heapdump
的工具和教程也非常多比如:chrome、vscode、heapdump 這個開源函式庫。我用的 heapdump 庫做的網路教學非常多這裡不展開了。
局部性記憶體洩漏排查需要一定的記憶體洩漏排查經驗,每次遇到都把它當成對自己的一次磨礪,這樣的經驗累積多了以後排查記憶體洩漏問題會越來越快。
1. 確定記憶體洩漏出現的時間範圍
這一點非常重要,明確了這一點可以大幅縮小排查範圍。
經常會出現這種情況,這個迭代做了A、B、C 三個功能,壓測時或上線後出現了記憶體洩漏。那就可以直接鎖定,記憶體洩漏發生小這三個新的功能之中。這種情況下就不需要非常麻煩的去生產做 heapdump
我們在本地通過一些工具就可以很輕鬆的分析定位出內存洩漏點。
由於我們20年Q4
的一些特殊情況,當我們發現存在記憶體洩漏的時候已經很難確定記憶體洩漏初次出現在什麼時間點了,只能大概鎖定在1月的時間內。這一個月我們又經歷了一個大版本迭代,如果一一排查這些功能與介面成本必然非常高。
所以還需要結合更多的數據進行進一步分析
2. 採集heapdump 數據
- 生產啟動時
node
新增--expose-gc
,這個參數會向全域注入gc()
方法,方便手動觸發GC 取得更準確的堆快照
資料 - 這裡我加入了兩個介面並帶上了自己的專屬權限,
- 手動觸發GC
- 列印堆快照
-
heapdump
- 專案啟動後第一次列印快照資料
- 記憶體上漲100M 後:先觸發GC,再第二次列印堆快照資料
- 記憶體接近臨界時再次觸發GC 然後列印堆疊快照
擷取堆快照資料時需要特別注意的一些點!
- 在
heapdump
時 Node 服務會中斷,根據當時伺服器記憶體大小這個時間會在 2 ~ 30min 左右。在生產環境做heapdump
需要和維運一起制定合理的策略。我在這裡是使用了主、備兩個pod
, 當主pod
停掉之後,業務請求會透過負載平衡到備用pod
由此保障生產業務的正常進行。 (這個過程必定是一個與維運密切配合的過程,畢竟heapdump
玩抽還需要透過他們拿到伺服器中堆快照
檔)- 上述接近臨界點列印快照只是一個模糊的描述,如果你試過就知道等非常接近臨界點再打印內存快照就打印不出來了。所以接近這個度需要自己把握。
- 做至少3 次
heapdump
(實際上為了拿到最詳細的資料我做了5 次)
#3 . 結合監控面板的數據進行分析
需要你的應用服務接入監控,我這裡應用是使用prometheus
grafana
做的監控, 主要監控服務的以下指標
-
QPS
(每秒請求存取量) ,請求狀態,及其存取路徑 - ##ART
(平均介面回應時間) 及其存取資料
- NodeJs
版本
- #Actice Handlers
(句柄)
##Event Loop Lag - (事件延遲)
服務程序重啟次數
- CPU 使用率
- 記憶體使用: rss
- 、
heapTotal
、heapUsed
、external
、heapAvailableDetail
##只有
資料是不夠的,heapdump
我的分析處理結果資料非常晦澀,就算在視覺化工具的加持下也難以準確定位問題。這時候我是結合了
grafana的一些數據一起看。
由於當時的對快照資料遺失了,我在這裡模擬當時的場景。
1、透過grafana
監控面看看到記憶體一直在漲一直下不來,但同時我也注意到,服務中的句柄數也在瘋漲一直不掉。
2、這是我回顧了一下出現洩漏的那一個月新增的功能懷疑可能是在使用 bull
訊息佇列元件造成的記憶體洩漏。先去分析了相關應用程式碼,但並看不出那裡寫的有問題導致了記憶體洩漏,
結合 1 中句柄洩漏的問題感覺是在使用 bull
後需要手動的去釋放某些資源,在這個時候還不太確定具體原因。
3、然後對5 次的heapdunmp
資料進行了分析,資料匯入chrome
對5 次堆快照進行比較後,發現每次建立佇列後TCP 、Socket、EventEmitter 的事件都沒有被釋放到。到這裡基本可以確定是由於對 bull
的使用不規範導致的。在 bull
通常不會經常建立佇列,佇列佔用的系統資源並不會自動釋放,若有需要,需手動釋放。
4、在調整完程式碼後重新進行了壓測,問題解決。
Tips: Nodejs 中的
句柄
是一種指針,指向底層系統資源(如檔案、網路連接等)。句柄允許 Node.js 程式存取和操作這些資源,而無需直接與底層系統互動。句柄可以是整數或對象,這取決於 Node.js 函式庫或模組使用的句柄類型。常見句柄
:
fs.open()
傳回的檔案句柄net.createServer()
傳回的網頁伺服器句柄dgram.createSocket()
傳回的UDP socket 帳號child_process.spawn()
傳回的子程序句柄crypto.createHash()
傳回的雜湊句柄zlib.createGzip()
傳回的壓縮句柄
#heapdump 分析總結
通常很多人第一次拿到堆快照
資料是懵的,我也是。在看了網路上無數的分析技巧結合自身實戰後總結了一些比較好用的技巧,一些基礎的使用教程這裡就不講了。這裡主要講資料導入chrome
後如何看圖;
Summary 視圖
看這個視圖的時候一般會先對Retained Size 進行排查,然後觀察其中物件的大小與數量,有經驗的工程師,可以快速判斷出某些物件數量異常。在這個視圖中除了關心自己定義的一些物件之外,
一些容易發生記憶體洩漏的物件也需要注意如:
TCP
Socket
- ## EventEmitter
- global
Comparison 視圖
如果透過Summary 視圖, 不能定位到問題這時我們一般會使用
Comparison 視圖。透過這個視圖我們能對比兩個堆快照中物件個數、與物件佔有記憶體的變化;
透過這些資訊我們可以判斷在一段時間(某些操作)之後,堆中的物件與記憶體變化的數值,透過這些數值我們可以找出一些異常的物件。透過這些物件的名稱屬性或作用可以縮小我們記憶體洩漏的排查範圍。
Comparison 視圖中選擇兩個堆疊快照,並在它們之間進行比較。您可以查看哪些物件在兩個堆快照之間新增,哪些物件在兩個堆快照之間減少,以及哪些物件的大小發生了變化。
Comparison 視圖還允許查看物件之間的關係,以及物件的詳細信息,例如類型、大小和引用計數。透過這些訊息,可以了解哪些物件是導致記憶體洩漏的原因。
Containment 視圖
#顯示了物件之間的所有可達的參考關係。每個物件都被表示為一個圓點,並由一條線條連接到它的父物件。透過這種方式可以查看物件之間的層次關係,並了解哪些物件是導致記憶體洩漏的原因。Statistics 視圖
這個圖很簡單地不展開講了記憶體洩漏場景
- 全域變數:全域變數不會被回收
- #快取:使用了記憶體密集的第三方函式庫如
lru-cache
存的太多就會導致記憶體不夠用,在Nodejs 服務中建議使用redis
替代lru-cache
##「句柄洩漏:調用完系統資源沒有釋放 - 事件監聽
- 閉包
- 循環引用
##服務需要存取監控,方便第一時間確定問題類型
#判斷記憶體洩漏是全域性的還是局部性的
#全域性記憶體洩漏使用二分法快速排查定位
-
局部記憶體洩漏
- 確定記憶體洩漏出現時間點,快速定位問題功能
- 採堆快照數據,至少3 次
- 結合監控數據、堆快照數據、與出現洩漏事時間點內的新功能對記憶體洩漏點進行定位
遇到記憶體洩漏的問題不要畏懼,多累積記憶體洩漏問題的排查經驗處理經驗多了找起來就非常快了。每次解決之後做複盤總結回頭再多看看
堆快照
數據利於更快的積累相關經驗
其它
- #壓測工具:wrk
更多node相關知識,請造訪:nodejs 教學!
以上是【經驗總結 】Node怎麼排查記憶體洩漏?思路分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)