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中文網其他相關文章!

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

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

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

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

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

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

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

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

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

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