目錄
問題描述
#記憶體洩漏類型
1. 確定記憶體洩漏出現的時間範圍
2. 採集heapdump 數據
#3 . 結合監控面板的數據進行分析
由於當時的對快照資料遺失了,我在這裡模擬當時的場景。
#heapdump 分析總結
Summary 視圖
記憶體洩漏場景
其它
首頁 web前端 js教程 【經驗總結 】Node怎麼排查記憶體洩漏?思路分享

【經驗總結 】Node怎麼排查記憶體洩漏?思路分享

Jan 28, 2023 pm 07:25 PM
前端 node.js

Node怎麼排查記憶體洩漏?以下這篇文章就來給大家整理總結一下Node記憶體洩漏排查經驗,希望對大家有幫助!

【經驗總結 】Node怎麼排查記憶體洩漏?思路分享

Nodejs 服務端開發的場景中,記憶體洩漏 絕對是最令人頭痛的問題; 但只要專案一直在開發迭代,那麼出現 記憶體洩漏 的問題絕對不可避免,只是出現的時間早晚而已。所以系統性掌握有效的 記憶體洩漏 排查方法是一名Nodejs 工程師最基礎、最核心的能力。

記憶體洩漏處理的困難是如何能在無數的功能、函數中找到具體是哪一個功能中的哪一個函數的第多少行到多少行引起了記憶體洩漏。 很遺憾目前市面上沒有能夠輕鬆定位記憶體洩漏的工具,所以許多初次遇到這種問題的工程師會感到茫然,一下子不知道該如何處理。 這裡我以22年的一次排查 記憶體洩漏 的案例分享一下我的處理想法。

問題描述

2022 Q4 某天,研發用戶群中回饋我們的研發平台不能訪問,後台中出現了大量的異常任務未完成。 第一反應就是可能出現了記憶體洩漏還好服務接入了監控(prometheus grafana),在grafana 監控面板中發現在10.00 後記憶體一直在漲沒有下來過出現了明顯的資料外洩。 【相關教學推薦:nodejs影片教學

DX 内存泄漏监控图.png

說明

  • #process memory: rss (Resident Set Size),進程的常駐記憶體大小。
  • heapTotal: V8 堆的總大小。
  • heapUsed: V8 堆已使用的大小。
  • external: V8 堆外的記憶體使用量。

Nodejs 中可以呼叫全域方法process.memoryUsage() 取得這些資料其中heapTotalheapUsed 是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
  • heapTotalheapUsedexternalheapAvailableDetail
  • ##只有
heapdump
資料是不夠的,

heapdump 資料非常晦澀,就算在視覺化工具的加持下也難以準確定位問題。這時候我是結合了 grafana 的一些數據一起看。

我的分析處理結果

由於當時的對快照資料遺失了,我在這裡模擬當時的場景。

1、透過

grafana

監控面看看到記憶體一直在漲一直下不來,但同時我也注意到,服務中的

句柄數也在瘋漲一直不掉。

2、這是我回顧了一下出現洩漏的那一個月新增的功能懷疑可能是在使用 bull 訊息佇列元件造成的記憶體洩漏。先去分析了相關應用程式碼,但並看不出那裡寫的有問題導致了記憶體洩漏, 結合 1 中句柄洩漏的問題感覺是在使用 bull 後需要手動的去釋放某些資源,在這個時候還不太確定具體原因。

3、然後對5 次的heapdunmp 資料進行了分析,資料匯入chrome 對5 次堆快照進行比較後,發現每次建立佇列後TCP 、Socket、EventEmitter 的事件都沒有被釋放到。到這裡基本可以確定是由於對 bull 的使用不規範導致的。在 bull 通常不會經常建立佇列,佇列佔用的系統資源並不會自動釋放,若有需要,需手動釋放。

【經驗總結 】Node怎麼排查記憶體洩漏?思路分享

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 視圖

【經驗總結 】Node怎麼排查記憶體洩漏?思路分享看這個視圖的時候一般會先對Retained Size 進行排查,然後觀察其中物件的大小與數量,有經驗的工程師,可以快速判斷出某些物件數量異常。在這個視圖中除了關心自己定義的一些物件之外, 一些容易發生記憶體洩漏的物件也需要注意如:

  • TCP
  • Socket
  • ## EventEmitter
  • global

Comparison 視圖

如果透過

Summary 視圖, 不能定位到問題這時我們一般會使用Comparison 視圖。透過這個視圖我們能對比兩個堆快照中物件個數、與物件佔有記憶體的變化; 透過這些資訊我們可以判斷在一段時間(某些操作)之後,堆中的物件與記憶體變化的數值,透過這些數值我們可以找出一些異常的物件。透過這些物件的名稱屬性或作用可以縮小我們記憶體洩漏的排查範圍。

Comparison 視圖中選擇兩個堆疊快照,並在它們之間進行比較。您可以查看哪些物件在兩個堆快照之間新增,哪些物件在兩個堆快照之間減少,以及哪些物件的大小發生了變化。 Comparison 視圖還允許查看物件之間的關係,以及物件的詳細信息,例如類型、大小和引用計數。透過這些訊息,可以了解哪些物件是導致記憶體洩漏的原因。

【經驗總結 】Node怎麼排查記憶體洩漏?思路分享

Containment 視圖

#顯示了物件之間的所有可達的參考關係。每個物件都被表示為一個圓點,並由一條線條連接到它的父物件。透過這種方式可以查看物件之間的層次關係,並了解哪些物件是導致記憶體洩漏的原因。

【經驗總結 】Node怎麼排查記憶體洩漏?思路分享

Statistics 視圖

這個圖很簡單地不展開講了

【經驗總結 】Node怎麼排查記憶體洩漏?思路分享

記憶體洩漏場景

  • 全域變數:全域變數不會被回收
  • #快取:使用了記憶體密集的第三方函式庫如lru-cache 存的太多就會導致記憶體不夠用,在Nodejs 服務中建議使用redis 替代lru-cache
  • ##「句柄洩漏:調用完系統資源沒有釋放
  • 事件監聽
  • 閉包
  • 循環引用
##總結

  • ##服務需要存取監控,方便第一時間確定問題類型

  • #判斷記憶體洩漏是全域性的還是局部性的

  • #全域性記憶體洩漏使用二分法快速排查定位

  • 局部記憶體洩漏

    • 確定記憶體洩漏出現時間點,快速定位問題功能
    • 採堆快照數據,至少3 次
    • 結合監控數據、堆快照數據、與出現洩漏事時間點內的新功能對記憶體洩漏點進行定位

遇到記憶體洩漏的問題不要畏懼,多累積記憶體洩漏問題的排查經驗處理經驗多了找起來就非常快了。每次解決之後做複盤總結回頭再多看看堆快照 數據利於更快的積累相關經驗

其它

  • #壓測工具:wrk

更多node相關知識,請造訪:nodejs 教學

以上是【經驗總結 】Node怎麼排查記憶體洩漏?思路分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

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

PHP與Vue:完美搭檔的前端開發利器 PHP與Vue:完美搭檔的前端開發利器 Mar 16, 2024 pm 12:09 PM

PHP與Vue:完美搭檔的前端開發利器在當今網路快速發展的時代,前端開發變得愈發重要。隨著使用者對網站和應用的體驗要求越來越高,前端開發人員需要使用更有效率和靈活的工具來創建響應式和互動式的介面。 PHP和Vue.js作為前端開發領域的兩個重要技術,搭配起來可以稱得上是完美的利器。本文將探討PHP和Vue的結合,以及詳細的程式碼範例,幫助讀者更好地理解和應用這兩

如何使用 Go 語言進行前端開發? 如何使用 Go 語言進行前端開發? Jun 10, 2023 pm 05:00 PM

隨著網路技術的發展,前端開發變得日益重要。尤其是行動端設備的普及,更需要高效率、穩定、安全又易於維護的前端開發技術。而作為一門快速發展的程式語言,Go語言已經被越來越多的開發者所使用。那麼,使用Go語言進行前端開發行得通嗎?接下來,本文將為你詳細說明如何使用Go語言進行前端開發。先來看看為什麼要使用Go語言進行前端開發。很多人認為Go語言是一門

Django是前端還是後端?一探究竟! Django是前端還是後端?一探究竟! Jan 19, 2024 am 08:37 AM

Django是一個由Python編寫的web應用框架,它強調快速開發和乾淨方法。儘管Django是web框架,但要回答Django是前端還是後端這個問題,需要深入理解前後端的概念。前端是指使用者直接和互動的介面,後端是指伺服器端的程序,他們透過HTTP協定進行資料的互動。在前端和後端分離的情況下,前後端程式可以獨立開發,分別實現業務邏輯和互動效果,資料的交

C#開發經驗分享:前端與後端協同開發技巧 C#開發經驗分享:前端與後端協同開發技巧 Nov 23, 2023 am 10:13 AM

身為C#開發者,我們的開發工作通常包括前端和後端的開發,而隨著技術的發展和專案的複雜性提高,前端與後端協同開發也變得越來越重要和複雜。本文將分享一些前端與後端協同開發的技巧,以幫助C#開發者更有效率地完成開發工作。確定好介面規範前後端的協同開發離不開API介面的交互。要確保前後端協同開發順利進行,最重要的是定義好介面規格。接口規範涉及到接口的命

前端怎麼實現即時通訊 前端怎麼實現即時通訊 Oct 09, 2023 pm 02:47 PM

實作即時通訊的方法有WebSocket、Long Polling、Server-Sent Events、WebRTC等等。詳細介紹:1、WebSocket,它可以在客戶端和伺服器之間建立持久連接,實現即時的雙向通信,前端可以使用WebSocket API來創建WebSocket連接,並透過發送和接收訊息來實現即時通訊;2、Long Polling,是一種模擬即時通訊的技術等等

前端面試官常問的問題 前端面試官常問的問題 Mar 19, 2024 pm 02:24 PM

在前端開發面試中,常見問題涵蓋廣泛,包括HTML/CSS基礎、JavaScript基礎、框架和函式庫、專案經驗、演算法和資料結構、效能最佳化、跨域請求、前端工程化、設計模式以及新技術和趨勢。面試官的問題旨在評估候選人的技術技能、專案經驗以及對行業趨勢的理解。因此,應試者應充分準備這些方面,以展現自己的能力和專業知識。

Go語言前端技術探秘:前端開發新視野 Go語言前端技術探秘:前端開發新視野 Mar 28, 2024 pm 01:06 PM

Go語言作為一種快速、高效的程式語言,在後端開發領域廣受歡迎。然而,很少有人將Go語言與前端開發聯繫起來。事實上,使用Go語言進行前端開發不僅可以提高效率,還能為開發者帶來全新的視野。本文將探討使用Go語言進行前端開發的可能性,並提供具體的程式碼範例,幫助讀者更了解這一領域。在傳統的前端開發中,通常會使用JavaScript、HTML和CSS來建立使用者介面

Django:前端和後端開發都能搞定的神奇框架! Django:前端和後端開發都能搞定的神奇框架! Jan 19, 2024 am 08:52 AM

Django:前端和後端開發都能搞定的神奇框架! Django是一個高效、可擴展的網路應用程式框架。它能夠支援多種Web開發模式,包括MVC和MTV,可以輕鬆地開發出高品質的Web應用程式。 Django不僅支援後端開發,還能夠快速建構出前端的介面,透過模板語言,實現靈活的視圖展示。 Django把前端開發和後端開發融合成了一種無縫的整合,讓開發人員不必專門學習

See all articles