node.js 內存泄漏的祕密

node.js 內存泄漏的祕密

瘋狂的技術宅 前端先鋒 2前端

翻譯:瘋狂的技術宅
做者:Giovanny Gongora
來源:nodesource
正文共:3955 字
預計閱讀時間:10分鐘node

node.js 內存泄漏的祕密
一直以來,跟蹤 Node.js 的內存泄漏是一個反覆出現的話題,人們始終但願對其複雜性和緣由瞭解更多。json

並不是全部的內存泄漏都顯而易見。可是,一旦咱們肯定了其模式,就必須在內存使用率,內存中保存的對象和響應時間之間尋找關聯。在檢查對象時,應該根據本身所用的框架或技術(例如服務器端渲染),研究收集了多少對象,以及它們是否正常。但願在完成本文結束以後,你將可以理解並尋找一種策略來調試 Node.js 程序的內存消耗。服務器

Node.js 中的垃圾回收機制

JavaScript 是一種垃圾回收語言,而 Google 的 V8 最初是爲 Google Chrome 建立的JavaScript引擎,在許多狀況下均可以用做獨立的運行時。Node.js 中垃圾收集器的兩個重要操做是:markdown

  1. 肯定有用的或無用的對象,而且
  2. 回收或重用無用對象所佔用的內存。
    須要記住的要點:在垃圾回收器運行時,它將徹底暫停你的程序,直到完成工做爲止。所以,你須要經過維護對象的引用來最大程度地減小其工做。

V8 JavaScript 引擎會自動分配和取消分配 Node.js 進程使用的全部內存。讓咱們看看實際狀況是怎樣的。閉包

若是你將內存視爲一個樹結構,那麼能夠想象 V8 從「根節點」開始保存程序中全部的變量。這多是你的 window 對象,也多是 Node.js 模塊中的全局對象,一般稱爲控制者。須要牢記的一點是,你沒法對怎樣取消分配「根」節點進行控制。
node.js 內存泄漏的祕密app

接下來,你將找到一個 Object 節點,一般被稱爲葉子(沒有子引用的節點)。最後 JavaScript 中有 4 種數據類型:布爾值,字符串,數字和對象。框架

V8 將遍歷該樹並嘗試識別沒法從「根」節點訪問的數據組。若是沒法從「根」節點訪問該數據,則 V8 假定再也不使用該數據,並釋放內存。請記住:要肯定某個對象是否處於活動狀態,須要檢查是否可經過被定義爲活動對象的某個指針鏈到達;其餘全部的狀況,例如沒法從根節點訪問,或沒法被根節點或另外一個活動對象引用的對象,都會被視爲垃圾。dom

簡而言之,垃圾收集器有兩個主要任務:ide

  1. 跟蹤
  2. 計算對象之間的引用。
    當你須要跟蹤來自另外一個進程的遠程引用時,它可能會變得很棘手,可是在 Node.js 程序中,咱們一般用單進程,這樣使咱們更加輕鬆。

V8 的內存方案

V8 使用相似於 Java 虛擬機的方案,並將內存劃分爲多個段。實現這種包裝方案的東西被稱爲「駐留集」,它是指在 RAM 中駐留的進程所佔用的內存部分。

在駐留集中,你會發現:

  • 代碼段:代碼實際執行的位置。
  • 棧: 包含局部變量和全部值類型,其指針引用堆上的對象或定義程序的控制流。
  • 堆: 專門用於存儲引用類型(如對象、字符串和閉包)的內存段。
    node.js 內存泄漏的祕密
    還有重要的兩點要記住:

  • 對象的淺大小:保存對象自己所需的內存大小
  • 對象的保留大小:當刪除對象及其依賴對象時,被釋放的內存大小
    Node.js 有一個對象,以字節爲單位描述 Node.js 進程的內存使用狀況。在對象內部,你會發現:

  • rss: 是指駐留集大小。
  • heapTotal 和 heapUsed: 是指 V8 的內存使用狀況。
  • external: 是指與 V8 所管理的 JavaScript 對象綁定的 C++ 對象的內存使用狀況。

    查找泄漏

Chrome DevTools 是一個很棒的工具,可用於經過遠程調試來診斷 Node.js 程序中的內存泄漏。也有其餘爲你提供相似功能的工具。可是,你須要記住,概要分析是一項繁重的 CPU 任務,可能會對你的程序產生負面影響,必定要注意這一點!

咱們將要介紹的 Node.js 程序是一個簡單的 HTTP API Server,它具備多個端點,向使用該服務的人返回不一樣的信息。你能夠克隆這個程序的repository。

1const http = require('http')
 2
 3const leak = []
 4
 5function requestListener(req, res) {
 6
 7  if (req.url === '/now') {
 8    let resp = JSON.stringify({ now: new Date() })
 9    leak.push(JSON.parse(resp))
10    res.writeHead(200, { 'Content-Type': 'application/json' })
11    res.write(resp)
12    res.end()
13  } else if (req.url === '/getSushi') {
14    function importantMath() {
15      let endTime = Date.now() + (5 * 1000);
16      while (Date.now() < endTime) {
17        Math.random();
18      }
19    }
20
21    function theSushiTable() {
22      return new Promise(resolve => {
23        resolve('
相關文章
相關標籤/搜索