筆記:記一次解決V8使用內存超過默認限制

原由: 前端

混合TypeScript和javaScript開發,完美升級老項目,這個老項目是一個巨無霸項目,很是龐大,是集團公司的最核心項目java


遇到問題: node

webpack打包時候遇到webpack

對於曾經開發過C++,addon的我,熟悉的味道,下面有一些v8的字符出現,感受應該是v8層面出現了問題c++


報錯解決: git

任何報錯,先看第一個報錯,解決頂部的報錯。github


問題定位: web

JS堆棧跟蹤,javaScript heap out of memory ,內存不足面試


隱約記得,v8對使用內存的限制,64位系統是1.4G,32位系統是0.7G,Buffer屬於C++層面,不會被限制。可是長度會被限制,當Buffer被建立時候就會被判斷長度npm

https://github.com/nodejs/node/blob/master/lib/buffer.js#L90-L101

node.js官網對長度的文檔描述:

這種簡單問題不作闡述,繼續


項目以前純js開發,如今接入ts,爲何一樣的電腦,以前能夠運行,如今卻內存不足?

答案:

  1. 首先要從內存回收提及,爲何要限制內存使用,由於1.4G廣泛夠用,再一個,內存回收是會阻塞主線程。300MB大概是0.5s,這裏在我開發桌面端即時通信應用時,常常會遇到這個問題。一個20萬人的羣,一直以1000條/秒的速度推送消息到桌面端,時間一長,Node接入主進程層面就不行了。CPU和內存佔用會飆升,要作不少特殊優化處理
  2. 我在ts中配置容許使用js,那麼意味着要增長一個編譯ts成js的過程,這個編譯過程確定要佔用大部份內存。因此以前純js項目不會出現這個問題,
  3. 網上大部分都是手動更改webpack的源碼文件,達到修改v8使用內存限制的目的,可是做爲跨平臺的產品來講,必須支持兩點:工程化+自動化、可跨平臺無感知的狀況才能使用
  4. v8的內存回收機制影響,跟上面第二點搭配,固然,如今這套東西,已經被面試官問爛了(就跟考試讓你背古詩同樣),我面試是不會問這些無聊的問題。做爲一個Node.js的深度使用者,我以爲是C++出生的人,可能會在Node.js走得更遠,它更像是一個庫,一個前端製做工具的庫。若是要深刻後端,走得更遠,建議仍是要學習java與c++
    • *

解決辦法:

Node.js的8.0版本以上能夠這樣調整

export NODE_OPTIONS=--max_old_space_size=4096

也可使用自動化、工程化配置插件

increase-memory-limit

因爲一些部署服務器上的配置未知,在測試事後,我選擇了後者,編寫了新的構建命令,這樣達到效果。


難道作API工程師,不可能的,個人原則,使用第三方庫,框架必須看它的 源碼實現,包括Node.js

increase-memory-limit

源碼只有幾十行代碼

#!/usr/bin/env node
const path = require('path');
const glob = require('glob');
const fs = require('fs');

const maxOldSpaceSize = process.env.LIMIT || 10240;
const cwd = process.cwd() + path.sep;

glob(path.join(cwd, "node_modules", ".bin", "*"), function (err, files) {

  files.forEach(file => {
    // readFileSync will crash on non-files. Skip over these
    let stat = fs.lstatSync(fs.realpathSync(file));
    if (!stat.isFile()) {
      return;
    }
    if (file.indexOf('increase-memory-limit') >= 0) {
      return;
    }
    // build scripts will hand in LIMIT via cross-env
    // avoid updating it while we are running it
    if (file.indexOf('cross-env') >= 0) {
      return;
    }
    let contents = fs.readFileSync(file).toString();
    let lines = contents.split('\n')

    let patchedContents = "";

    for (var index = 0; index < lines.length; index++) {
      var line = lines[index];
      if (line.startsWith("if [") || line.startsWith("@IF") || line.indexOf ('has_node') !== -1) {
        patchedContents += line + "\n";
      } else {
        patchedContents += line.replace(/node(\.exe)?\b(?: \-\-max\-old\-space\-size\=[0-9]+)?/, `node$1 --max-old-space-size=${maxOldSpaceSize}`) + "\n";
      }
    }

    fs.writeFileSync(file, patchedContents);
    console.log(`'${file.replace(cwd, "")}'`, "written successfully.");
  });

});

它依賴glob這個庫

https://www.npmjs.com/package/glob

首先讀取LIMIT配置

而後拿到node啓動的命令路徑(配置path.sep針對跨平臺,cwd返回的路徑作處理分隔)

var glob = require("glob")

經過glob這庫,傳入路徑和配置後,拿到包含文件數組,而後讀取文件流信息而且toString()

最核心的源碼文件就是下面這個

patchedContents += line + "\n";
    } else {
      patchedContents += line.replace(/node(\.exe)?\b(?: \-\-max\-old\-space\-size\=[0-9]+)?/, `node$1 --max-old-space-size=${maxOldSpaceSize}`) + "\n";
    }
  }

  fs.writeFileSync(file, patchedContents);
  console.log(`'${file.replace(cwd, "")}'`, "written successfully.");

經過正則將讀取的文件流信息,匹配相應的配置後,替換內容後同步寫入(由於必須同步寫入!!!不然項目沒法啓動,不能異步此處)


目前有一個系列寫做計劃,面試成長系列和踩坑成長系列同步進行,喜歡的話點個在看,關注下公衆號:前端巔峯

相關文章
相關標籤/搜索