在以前的一篇文章中, 咱們遇到了一個項目在構建時內存溢出
的問題。vue
當時的解決方案是: 直接調大 node 的內存限制,避免達到內存上限。
node
今天聽同事分享了一個新方法
,以爲不錯, 特此記錄, 順便分享給你們, 但願對你們有所幫助。webpack
直接上報錯示意圖:web
提示已經很明顯: Javascript Heap out of memory.
算法
看到內存溢出這個關鍵字,咱們通常都會考慮到是由於 Node.js 內存不夠致使的。segmentfault
但 Node 進程的內存限制
會是多少呢?閉包
在網上查閱了到以下描述:oop
Currently, by default V8 has a memory limit of 512mb on 32-bit systems, and 1gb on 64-bit systems. The limit can be raised by setting --max-old-space-size to a maximum of ~1gb (32-bit) and ~1.7gb (64-bit), but it is recommended that you split your single process into several workers if you are hitting memory limits.
翻譯一下:post
當前,默認狀況下,V8在32位系統上的內存限制爲512mb,在64位系統上的內存限制爲1gb。學習
能夠經過將
--max-old-space-size
設置爲最大〜1gb(32位)和〜1.7gb(64位)來提升此限制,可是若是達到內存限制, 建議您將單個進程
拆分爲多個工做進程
。
若是你想知道本身電腦的內存限制有多大, 能夠直接把內存撐爆, 看報錯。
運行以下代碼:
// Small program to test the maximum amount of allocations in multiple blocks. // This script searches for the largest allocation amount. // Allocate a certain size to test if it can be done. function alloc (size) { const numbers = size / 8; const arr = [] arr.length = numbers; // Simulate allocation of 'size' bytes. for (let i = 0; i < numbers; i++) { arr[i] = i; } return arr; }; // Keep allocations referenced so they aren't garbage collected. const allocations = []; // Allocate successively larger sizes, doubling each time until we hit the limit. function allocToMax () { console.log("Start"); const field = 'heapUsed'; const mu = process.memoryUsage(); console.log(mu); const gbStart = mu[field] / 1024 / 1024 / 1024; console.log(`Start ${Math.round(gbStart * 100) / 100} GB`); let allocationStep = 100 * 1024; // Infinite loop while (true) { // Allocate memory. const allocation = alloc(allocationStep); // Allocate and keep a reference so the allocated memory isn't garbage collected. allocations.push(allocation); // Check how much memory is now allocated. const mu = process.memoryUsage(); const gbNow = mu[field] / 1024 / 1024 / 1024; console.log(`Allocated since start ${Math.round((gbNow - gbStart) * 100) / 100} GB`); } // Infinite loop, never get here. }; allocToMax();
不出意外, 你將喜提以下報錯:
個人電腦是 Macbook Pro masOS Catalina 16GB,Node 版本是 v12.16.1,這段代碼大概在 1.6 GB 左右內存時候拋出異常。
那咱們如今知道 Node Process 確實是有一個內存限制的, 那咱們就來增大它的內存限制再試一下。
用 node --max-old-space-size=6000
來運行這段代碼,獲得以下結果:
內存達到 4.6G 的時候也溢出了。
你可能會問, node 不是有內存回收嗎?這個咱們在下面會講。
使用這個參數:node --max-old-space-size=6000
, 咱們增長的內存中老生代區域
的大小,比較暴力。
就像上文中提到的: 若是達到內存限制, 建議您將單個進程
拆分爲多個工做進程
。
這個項目是一個 ts 項目,ts 文件的編譯是比較佔用內存的,若是把這部分獨立成一個單獨的進程, 狀況也會有所改善。
由於 ts-loader
內部調用了 tsc
,在使用 ts-loader 時,會使用 tsconfig.js配置文件。
當項目中的代碼變的愈來愈多,體積也愈來愈龐大時,項目編譯時間
也隨之增長。
這是由於 Typescript 的語義檢查器
必須在每次重建時檢查全部文件
。
ts-loader
提供了一個 transpileOnly
選項,它默認爲 false
,咱們能夠把它設置爲 true
,這樣項目編譯時就不會進行類型檢查,也不會輸出聲明文件。
對一下 transpileOnly
分別設置 false
和 true
的項目構建速度對比:
雖然構建速度提高了,可是有了一個弊端: 打包編譯不會進行類型檢查
。
好在官方推薦了這樣一個插件, 提供了這樣的能力: fork-ts-checker-webpack-plugin
。
官方示例的使用也很是簡單:
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin') module.exports = { ... plugins: [ new ForkTsCheckerWebpackPlugin() ] }
在我這個實際的項目中,vue.config.js
修改以下:
configureWebpack: config => { // get a reference to the existing ForkTsCheckerWebpackPlugin const existingForkTsChecker = config.plugins.filter( p => p instanceof ForkTsCheckerWebpackPlugin, )[0]; // remove the existing ForkTsCheckerWebpackPlugin // so that we can replace it with our modified version config.plugins = config.plugins.filter( p => !(p instanceof ForkTsCheckerWebpackPlugin), ); // copy the options from the original ForkTsCheckerWebpackPlugin // instance and add the memoryLimit property const forkTsCheckerOptions = existingForkTsChecker.options; forkTsCheckerOptions.memoryLimit = 4096; config.plugins.push(new ForkTsCheckerWebpackPlugin(forkTsCheckerOptions)); }
修改以後, 構建就成功了。
在 Node.js 裏面,V8 自動幫助咱們進行垃圾回收, 讓咱們簡單看一下V8中如何處理內存。
常駐集大小:是RAM中保存的進程所佔用的內存部分,其中包括:
垃圾回收是回收由應用程序再也不使用的對象所佔用的內存的過程。
一般,內存分配很便宜,而內存池用完時收集起來很昂貴。
若是沒法從根節點訪問對象,則該對象是垃圾回收的候選對象,所以該對象不會被根對象或任何其餘活動對象引用。
根對象能夠是全局對象,DOM元素或局部變量。
堆有兩個主要部分,即 New Space
和 Old Space
。
新空間是進行新分配的地方。
在這裏收集垃圾的速度很快,大小約爲1-8MB
。
留存在新空間中的物體被稱爲新生代
。
在新空間中倖存下來的物體被提高的舊空間-它們被稱爲老生代
。
舊空間中的分配速度很快,可是收集費用很高,所以不多執行。
The V8 JavaScript engine employs a stop-the-world garbage collector mechanism.
In practice, it means that the program stops execution while garbage collection is in progress.
一般,約20%的年輕一代能夠存活到老一代,舊空間的收集工做將在耗盡後纔開始。
爲此,V8 引擎使用兩種不一樣的收集算法
:
新生代
上運行,老生代
上運行。篇幅有限,關於v8垃圾回收的更多信息,能夠參考以下文章:
小小總結一下,上文介紹了兩種方式:
node --max-old-space-size=4096
fork-ts-checker-webpack-plugin
但願你們留個印象, 記得這兩種方式。
好了, 內容就這麼多, 謝謝。
才疏學淺,若有錯誤, 歡迎指正。
謝謝。
若是以爲內容有幫助, 能夠關注下個人公衆號,掌握最新動態,一塊兒學習!