Linux 上的 Node.js 記憶體洩漏診斷 文章首圖

Linux 上的 Node.js 記憶體洩漏診斷

Linux 上的 Node.js 記憶體洩漏診斷

在現代 Web 開發中,Node.js 因其非阻塞 I/O 模型和高併發能力廣受歡迎。然而,隨著應用程式複雜度增加,記憶體洩漏(Memory Leak)往往成為導致服務不穩定、響應變慢甚至崩潰的隱形殺手。對於 Linux 環境下的 Node.js 開發者而言,掌握系統層級的診斷工具與 Runtime 層級的分析技巧至關重要。本文將引導中階使用者,透過實際指令與工具,逐步定位並解決記憶體洩漏問題。

第一步:監控基礎資源使用情況

當發現服務異常時,首先應確認問題是否確實來自於記憶體。在 Linux 終端中,我們可以結合 tophtop 觀察 Node.js 進程的 RSS(Resident Set Size,實際佔用的物理記憶體)。

若發現 RSS 持續增長且不釋放,這通常是記憶體洩漏的強烈訊號。為了更精確地監控,建議使用 pidstat 工具,它能提供歷史數據趨勢。

# 安裝 sysstat 套件以使用 pidstat
sudo apt update && sudo apt install -y sysstat

# 每 2 秒更新一次,觀察 PID 為 12345 的 Node.js 進程記憶體變化
# -r 顯示記憶體統計,-p 指定進程 ID
pidstat -r -p 12345 2

觀察 RSS 欄位,若其數值呈現單向上升且未見回落,即可初步判定存在記憶體洩漏風險。

第二步:生成堆疊快照(Heap Snapshot)

僅靠監控無法得知洩漏源頭,我們需要深入 Node.js 的堆疊(Heap)內部。Node.js 內建了 V8 引擎的堆疊分析功能,透過 v8-profiler-next 或內建的 --heapsnapshot 標記即可達成。對於生產環境,最安全且有效的方式是發送信號觸發快照,而非修改程式碼。

  1. 啟動 Node.js 時啟用堆疊快照支持: 確保你的啟動指令包含 --heapsnapshot-signal=SIGUSR2
node --heapsnapshot-signal=SIGUSR2 server.js
  1. 觸發快照生成: 當發現記憶體異常時,向進程發送 SIGUSR2 信號。
# 發送信號觸發快照
kill -USR2 <PID>

# 快照通常會生成在當前目錄,檔名類似 <PID>-<timestamp>.heapsnapshot
ls -l *.heapsnapshot
  1. 分析快照: 將生成的 .heapsnapshot 檔案下載至本地,使用 Chrome DevTools 打開。在 Chrome 中,打開 DevTools(F12),切換至 Memory 標籤頁,點擊 Load 並選擇該檔案。你可以比較兩次快照的 "Comparison" 視圖,找出增長最多的對象類型(如 Array、String 或 Closure),這通常能直接指向洩漏的程式碼位置。

常見問題與解決方案

1. 快照檔案過大,影響生產環境性能

在生產環境中,生成堆疊快照會導致主執行緒暫停(Stop-the-World),若堆疊過大,可能引發服務暫時不可用。

  • 解決方案:避免頻繁觸發快照。建議結合監控系統(如 Prometheus + Grafana),當記憶體使用率超過閾值(例如 80%)時,才自動觸發快照。此外,可考慮使用 clinic.js 等專業工具,它在分析過程中對性能的影響相對較小。

2. 無法確定是程式碼洩漏還是正常記憶體增長

有些應用本身就需要緩存大量數據(如 Redis 或 ORM 緩存),記憶體增長可能是正常行為。

  • 解決方案:使用 --max-old-space-size 限制 Node.js 的最大堆疊大小,並觀察 GC(垃圾回收)頻率。如果記憶體增長後能穩定在某一水平並隨 GC 波動,則可能是正常緩存;若持續增長直至 OOM(Out of Memory),則確認為洩漏。可使用 node --trace-gc 啟動應用,觀察 GC 日誌,若看到頻繁的 Full GC 但記憶體未釋放,即為典型洩漏特徵。

小結

診斷 Node.js 記憶體洩漏並非一蹴可幾,它需要結合 Linux 系統層的資源監控與 V8 引擎層級的深度分析。透過 pidstat 觀察趨勢,再配合 SIGUSR2 觸發堆疊快照進行比對,中階開發者足以應對大多數常見的洩漏場景。記住,預防勝於治療:在開發階段引入記憶體監控,並定期進行壓力測試,將能有效避免生產環境中的突發危機。