首頁 >web前端 >js教程 >Node.js 的記憶體限製到底是多少?

Node.js 的記憶體限製到底是多少?

Susan Sarandon
Susan Sarandon原創
2024-12-25 04:35:18856瀏覽

What Exactly Is the Memory Limit of Node.js?

熟練 Node.js API 可以讓您快速入門,但深入了解 Node.js 程式的記憶體佔用可以讓您走得更遠。

讓我們先透過 process.memoryUsage() 查看記憶體使用情況,每秒更新一次:

setInterval(() => { console.log('Memory Usage:', process.memoryUsage()); }, 1000);

由於輸出以位元組為單位,因此不方便使用者使用。讓我們透過將記憶體使用量格式化為 MB:
來修飾它

function formatMemoryUsageInMB(memUsage) {
    return {
        rss: convertToMB(memUsage.rss),
        heapTotal: convertToMB(memUsage.heapTotal),
        heapUsed: convertToMB(memUsage.heapUsed),
        external: convertToMB(memUsage.external)
    };
}

const convertToMB = value => {
    return (value / 1024 / 1024).toFixed(2) + ' MB';
};

const logInterval = setInterval(() => {
    const memoryUsageMB = formatMemoryUsageInMB(process.memoryUsage());
    console.log(`Memory Usage (MB):`, memoryUsageMB);
}, 1000);

現在,我們每秒都可以得到以下輸出:

Memory Usage (MB): {
  rss: '30.96 MB', // The actual OS memory used by the entire program, including code, data, shared libraries, etc.
  heapTotal: '6.13 MB', // The memory area occupied by JS objects, arrays, etc., dynamically allocated by Node.js
                      // V8 divides the heap into young and old generations for different garbage collection strategies
  heapUsed: '5.17 MB',
  external: '0.39 MB'
}

Memory Usage (MB): {
  rss: '31.36 MB',
  heapTotal: '6.13 MB',
  heapUsed: '5.23 MB',
  external: '0.41 MB'
}

我們都知道V8引擎的記憶體使用是有限的,不僅受到作業系統的記憶體管理和資源分配策略的限制,還受到其自身的設定的限制。

使用 os.freemem(),我們可以看到作業系統有多少可用內存,但這並不意味著 Node.js 程式可以取得所有記憶體。

console.log('Free memory:', os.freemem());

對於 64 位元系統,Node.js V8 預設最大舊空間大小約為 1.4GB。這意味著即使您的作業系統有更多可用內存,V8 也不會自動使用超過此限制的內存。

提示:可以透過設定環境變數或啟動 Node.js 時指定參數來變更此限制。例如,如果您希望 V8 使用更大的堆,可以使用 --max-old-space-size 選項:

node --max-old-space-size=4096 your_script.js

這個數值需要根據你的實際狀況和場景來設定。比如說,如果你有一台大記憶體的機器,獨立部署,還有很多小記憶體的機器分散部署,這個值的設定一定會有所不同。

讓我們透過無限地向數組填充資料來運行測試,直到記憶體溢出,看看什麼時候會發生。

const array = [];
while (true) {
    for (let i = 0; i < 100000; i++) {
        array.push(i);
    }
    const memoryUsageMB = formatMemoryUsageInMB(process.memoryUsage());
    console.log(`Memory Usage (MB):`, memoryUsageMB);
}

這是我們直接執行程式時得到的結果。新增一段資料後,程式崩潰了。

Memory Usage (MB): {
  rss: '2283.64 MB',
  heapTotal: '2279.48 MB',
  heapUsed: '2248.73 MB',
  external: '0.40 MB'
}
Memory Usage (MB): {
  rss: '2283.64 MB',
  heapTotal: '2279.48 MB',
  heapUsed: '2248.74 MB',
  external: '0.40 MB'
}


#
# Fatal error in , line 0
# Fatal JavaScript invalid size error 169220804
#
#
#
#FailureMessage Object: 0x7ff7b0ef8070

困惑嗎?不是限制1.4G嗎?為什麼要使用2G以上?實際上,Node.js 的 1.4GB 限制是 V8 引擎的歷史限制,適用於早期的 V8 版本和某些配置。在現代 Node.js 和 V8 中,Node.js 會根據系統資源自動調整其記憶體使用情況。在某些情況下,它可能使用遠超過 1.4GB 的空間,特別是在處理大型資料集或運行記憶體密集型操作時。

當我們將記憶體限制設為 512M 時,當 rss 達到 996 MB 左右時就會溢位。

Memory Usage (MB): {
  rss: '996.22 MB',
  heapTotal: '993.22 MB',
  heapUsed: '962.08 MB',
  external: '0.40 MB'
}
Memory Usage (MB): {
  rss: '996.23 MB',
  heapTotal: '993.22 MB',
  heapUsed: '962.09 MB',
  external: '0.40 MB'
}

<--- Last few GCs --->

[22540:0x7fd27684d000]     1680 ms: Mark-sweep 643.0 (674.4) -> 386.8 (419.4) MB, 172.2 / 0.0 ms  (average mu = 0.708, current mu = 0.668) allocation failure; scavenge might not succeed
[22540:0x7fd27684d000]     2448 ms: Mark-sweep 962.1 (993.2) -> 578.1 (610.7) MB, 240.7 / 0.0 ms  (average mu = 0.695, current mu = 0.687) allocation failure; scavenge might not succeed


<--- JS stacktrace --->

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

綜上所述,更準確的來說,Node.js 的記憶體限制是指堆記憶體限制,即 JS 物件、陣列等可以佔用的最大內存,由 V8 分配。

堆記憶體的大小決定了Node.js進程可以佔用多少記憶體嗎?不!繼續閱讀。

我可以將 3GB 檔案放入 Node.js 記憶體中嗎?

我們在測試中看到,在程式崩潰之前,陣列只能容納 2GB 多一點。那麼,如果我有一個 3GB 的文件,我不能把它一次放入 Node.js 記憶體嗎?

你可以的!

我們透過process.memoryUsage()看到了一個外部內存,它被Node.js進程佔用,但沒有被V8分配。只要你把3GB的檔案放在那裡,就沒有記憶體限制。如何?您可以使用緩衝區。 Buffer 是 Node.js 的 C 擴充模組,它使用 C 來分配內存,而不是 JS 物件和資料。

這是一個示範:

setInterval(() => { console.log('Memory Usage:', process.memoryUsage()); }, 1000);

即使你分配了3GB內存,我們的程式仍然可以順利運行,而我們的Node.js程式佔用了超過5GB內存,因為這個外部內存不是Node.js限制的,而是操作系統對分配內存的限製到線程(所以不能胡作非為,連Buffer 都會記憶體不足;本質是用Streams 來處理大數據)。

在 Node.js 中,Buffer 物件的生命週期與 JavaScript 物件相關聯。當 JavaScript 對 Buffer 物件的參考被刪除時,V8 垃圾收集器會將該物件標記為可回收,但 Buffer 物件的底層記憶體不會立即釋放。通常,當呼叫 C 擴展的析構函數時(例如 Node.js 中的垃圾回收過程),這部分記憶體會被釋放。然而,這個過程可能與V8的垃圾收集並不完全同步。

function formatMemoryUsageInMB(memUsage) {
    return {
        rss: convertToMB(memUsage.rss),
        heapTotal: convertToMB(memUsage.heapTotal),
        heapUsed: convertToMB(memUsage.heapUsed),
        external: convertToMB(memUsage.external)
    };
}

const convertToMB = value => {
    return (value / 1024 / 1024).toFixed(2) + ' MB';
};

const logInterval = setInterval(() => {
    const memoryUsageMB = formatMemoryUsageInMB(process.memoryUsage());
    console.log(`Memory Usage (MB):`, memoryUsageMB);
}, 1000);

總結:Node.js 記憶體使用量由 JS 堆記憶體使用量(由 V8 的垃圾回收決定)和 C 記憶體分配組成

為什麼堆記憶體分為新代和老代?

分代垃圾收集策略在現代程式語言的實作中非常普遍! Ruby、.NET 和 Java 中都可以找到類似分代垃圾收集的類似策略。當垃圾回收發生時,常常會導致「stop the world」的情況,這不可避免地影響程式效能。然而,這種設計是考慮到性能優化的。

  • 不同的物件壽命 在程式開發過程中,很大一部分變數是臨時的,用於完成特定的本地計算任務。這樣的變數更適合Minor GC,即新一代GC。新一代記憶體中的物件主要透過Scavenge演算法進行垃圾回收。 Scavenge 演算法將堆疊記憶體一分為二,即 From 和 To(經典的空間換時間權衡。由於它們的生存時間較短,因此不會消耗大量記憶體)。

記憶體分配時,發生在 From 內。在垃圾回收期間,會檢查 From 中的活動物件並將其複製到 To,然後釋放非活動物件。在下一輪收集中,To 中的活動物件將被複製到 From,此時 To 會轉變為 From,反之亦然。在每個垃圾收集週期中,From 和 To 都會交換。此演算法在複製過程中僅複製存活對象,從而避免記憶體碎片的產生。
那麼,變數的活躍度是如何決定的呢?可達性分析開始發揮作用。以以下物件為例:

  • globalObject:全域物件。
  • obj1:被globalObject直接引用的物件。
  • obj2:obj1所引用的物件。
  • obj3:一個孤立的對象,沒有任何其他對象的引用。

在可達性分析的背景下:

  • globalObject 作為根對象,本質上是可存取的。
  • obj1,由於被globalObject引用,也是可達的。
  • obj2 被 obj1 引用,因此也是可訪問的。
  • 相較之下,obj3 由於缺少根物件或其他可到達物件的任何引用路徑,因此被判定為不可到達,因此符合回收條件。

誠然,引用計數可以作為一種輔助手段。然而,在存在循環引用的情況下,它無法準確地確定物體的真實活性。

在老年代記憶體中,物件通常較不活躍。但當老年代記憶體滿了時,會透過Mark-Sweep演算法觸發老年代記憶體的清理(Major GC)。

標記-清除演算法包括兩個階段:標記和清除。在標記階段,V8 引擎會遍歷堆中的所有物件並標記存活的物件。在清掃階段,僅清除未標記的物件。此演算法的優點是,由於老年代中死亡對象的比例相對較小,因此清理階段消耗的時間相對較少。但它的缺點是只清除而不壓縮,可能會導致記憶體空間不連續,不方便為大物件分配記憶體。

這個缺點會導致記憶體碎片,需要使用另一種演算法,Mark-Compact。此演算法將所有存活物件移至一端,然後一舉消滅邊界右側的無效記憶體空間,從而獲得完整且連續的可用記憶體空間。它解決了 Mark-Sweep 演算法可能導致的記憶體碎片問題,但代價是移動大量活動物件會花費更多時間。

如果您覺得這篇文章有用,請按讚。 :D

以上是Node.js 的記憶體限製到底是多少?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

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