Node.js 內存溢出時如何處理?

Node.js 作密集型運算,或者所操做的數組、對象自己較大時,容易出現內存溢出的問題,這是因爲 Node.js 的運行環境依賴 V8 引擎致使的。若是常常有較大數據量運算等操做,須要對 Node.js 運行環境限制有充分的瞭解。javascript

本文涵蓋

  1. 內存溢出問題
  2. 爲何會內存溢出
    • 2.1 V8內存分配機制
    • 2.2 內存溢出的緣由
  3. 如何解決內存溢出問題

做者簡介:koala,專一完整的 Node.js 技術棧分享,從 JavaScript 到 Node.js,再到後端數據庫,祝您成爲優秀的高級 Node.js 工程師。【程序員成長指北】做者,Github 博客開源項目 github.com/koala-codin…java

1.內存溢出問題

下面是咱們在Node.js應用中常常遇到的兩類內存溢出問題:node

密集型運算

示例1:當咱們須要批量處理一些數據(如:更新用戶某項信息)時,咱們可能須要一個較大的for或while循環來完成全部的數據的更新,如:git

for (var i = 0; i < 10000000; i++) {
    ((i) => {
        var site = {};
        site.name = 'koala';
        site.domain = '程序員成長指北';
        // 這裏是一個保存或更新等操做

        setTimeout(()=>{
            console.log(i, site);
        }, 0)
    })(i)
}
複製代碼

操做的數據量較大

示例2:對象須要頻繁的建立/銷燬,或操做對象自己較大,如:程序員

var sites = [];
for (var x=0;x<5000;x++){
    var site=[];
    for (var y=0;y<5000;y++){
        site = [y, 'koala', '程序員成長指北'];
        sites.push(site);
    }
}
複製代碼

上面兩類操做都會出現相似如下錯誤:github

<--- Last few GCs --->
……
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory
Abort trap: 6
複製代碼

2. 爲何會內存溢出

2.1 V8內存分配機制

咱們都知道,V8是 Google 在 Chrome 瀏覽器中使用的 JavaScript 引擎。而在瀏覽器環境中,運算通常不須要多大內存。 V8 對每一個進程分配的運行內存,在32位系統中約爲700MB,而在64位系統中約爲1.4GB。數據庫

2.2 內存溢出的緣由

Node.js 程序之因此會出內存溢出的狀況,能夠分爲三方面的緣由:後端

    1. V8自己分配的內存較小
    1. JavaScript語言自己限制
    1. 程序員使用不當。

在示例1中,每次運算所需的內存量並不大,但因爲for循環,形成V8內存不能及時釋放。隨着程序運行時候的增長,內存佔用量會愈來愈大,並最終致使內存的溢出。數組

在示例2中,可能所建立對象自己並無超過內存限制。可是除對象自己外:建立對象、對象引用、Node.js程序自己等都須要內存空間,這樣就很容易致使內存的溢出。瀏覽器

3. 解決內存溢出問題

在Node.js應用開發過程當中,瞭解V8內存分配和JavaScript語言限制是Node程序的基本素質。咱們應該在應用中權衡利弊,綜合考慮內存與程序的運行效率。如下幾點防止內存溢出的建議:

3.1. 使用 async/await防止事件堆積,變爲同步操做

await將代碼執行順序變爲了同步。這樣可使 V8 得到內存回收的機會,有效解決過多事件堆積形成的內存溢出。 咱們可使用await方法處理:

async function dbFuc() {
for (let i = 0; i < 10000000; i++) {
    var site = {};
    site.name = 'koala';
    site.domain = '程序員成長指北';
    // 這裏是一個保存或更新等操做

    await  console.log(i, site);

    }
}
dbFuc();
複製代碼

每次循環V8都會回收內存一次,所以內存不會再溢出。但這樣作必然會形成運行效率的下降,而應該在速度在安全之間平衡,控制好循環的安全次數。 說明:實際開發中,上面這種雖然解決了內存溢出,可是仍然會形成進程阻塞,能夠開啓一個進程/線程來解決阻塞問題(具體能夠看個人這篇文章《深刻理解Node.js 進程與線程(8000長文完全搞懂)》)

3.2. 增長V8內存空間

Node.js提供了一個程序運行參數--max-old-space-size,能夠經過該參數指定V8所佔用的內存空間,這樣能夠在必定程度上避免程序內存的溢出。 如,咱們能夠在運行示例2程序時指定使用4G的內存: node --max-old-space-size=4096 app

3.3. 使用非V8內存

Node.js程序所使用的內存分爲兩類:

  • V8內存:數組、字符串等JavaScript內置對象,運行時使用「V8內存」
  • 系統內存:Buffer 是 Node.js 的一個擴展對象,使用底層的系統內存,不佔用V8內存空間。與 buffer 相關的文件系統 fs 和stream 流操做,都不會佔用 V8 內存。

(注: fs 和 stream 這兩個模塊我在 Node 進階系列文章中已經詳細介紹了, 這裏就不贅述)

在程序容許的狀況下,應該將數據保存在Buffer中,而不是轉換成字符串等JS對象,這樣能夠避免V8內存的過多佔用。(buffer能夠看一下這篇文章《Node進階-探究不在V8堆內存中存儲的Buffer對象》

Node系列原創文章

深刻理解Node.js 中的進程與線程

想學Node.js,stream先有必要搞清楚

require時,exports和module.exports的區別你真的懂嗎

源碼解讀一文完全搞懂Events模塊

Node.js 高級進階之 fs 文件模塊學習

關注我

  • 歡迎加我微信(coder_qi),拉你進技術羣,長期交流學習...
  • 歡迎關注「程序員成長指北」,一個用心幫助你成長的公衆號...
相關文章
相關標籤/搜索