<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          項(xiàng)目構(gòu)建內(nèi)存溢出了?看看 Node 內(nèi)存限制

          共 6914字,需瀏覽 14分鐘

           ·

          2021-05-12 22:16

          背景

          在之前的一篇文章中, 我們遇到了一個(gè)項(xiàng)目在構(gòu)建時(shí)內(nèi)存溢出的問題。

          當(dāng)時(shí)的解決方案是: 直接調(diào)大 node 的內(nèi)存限制,避免達(dá)到內(nèi)存上限。

          今天聽同事分享了一個(gè)新方法,覺得不錯(cuò), 特此記錄, 順便分享給大家。

          正文

          報(bào)錯(cuò)示意圖:

          提示已經(jīng)很明顯:Javascript Heap out of memory.

          看到內(nèi)存溢出這個(gè)關(guān)鍵字,我們一般都會考慮到是因?yàn)?Node.js 內(nèi)存不夠?qū)е碌摹?/p>

          但 Node 進(jìn)程的內(nèi)存限制會是多少呢?

          在網(wǎng)上查閱了到如下描述:

          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.

          翻譯一下:

          當(dāng)前,默認(rèn)情況下,V8在32位系統(tǒng)上的內(nèi)存限制為512mb,在64位系統(tǒng)上的內(nèi)存限制為1gb。

          可以通過將--max-old-space-size設(shè)置為最大?1gb(32位)和?1.7gb(64位)來提高此限制,但是如果達(dá)到內(nèi)存限制, 建議您將單個(gè)進(jìn)程拆分為多個(gè)工作進(jìn)程。

          如果你想知道自己電腦的內(nèi)存限制有多大, 可以直接把內(nèi)存撐爆, 看報(bào)錯(cuò)。

          運(yùn)行如下代碼:

          // 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();

          不出意外, 你將喜提如下報(bào)錯(cuò):

          我的電腦是 Macbook Pro masOS Catalina 16GB,Node 版本是 v12.16.1,這段代碼大概在 1.6 GB 左右內(nèi)存時(shí)候拋出異常。

          那我們現(xiàn)在知道 Node Process 確實(shí)是有一個(gè)內(nèi)存限制的, 那我們就來增大它的內(nèi)存限制再試一下。

          node --max-old-space-size=6000 來運(yùn)行這段代碼,得到如下結(jié)果:

          內(nèi)存達(dá)到 4.6G 的時(shí)候也溢出了。

          你可能會問, node 不是有內(nèi)存回收嗎?這個(gè)我們在下面會講。

          使用這個(gè)參數(shù):node --max-old-space-size=6000, 我們增加的內(nèi)存中老生代區(qū)域的大小,比較暴力。

          就像上文中提到的:如果達(dá)到內(nèi)存限制, 建議您將單個(gè)進(jìn)程拆分為多個(gè)工作進(jìn)程。

          這個(gè)項(xiàng)目是一個(gè) ts 項(xiàng)目,ts 文件的編譯是比較占用內(nèi)存的,如果把這部分獨(dú)立成一個(gè)單獨(dú)的進(jìn)程, 情況也會有所改善。

          因?yàn)?ts-loader 內(nèi)部調(diào)用了 tsc,在使用 ts-loader 時(shí),會使用 tsconfig.js配置文件。

          當(dāng)項(xiàng)目中的代碼變的越來越多,體積也越來越龐大時(shí),項(xiàng)目編譯時(shí)間也隨之增加。

          這是因?yàn)?Typescript 的語義檢查器必須在每次重建時(shí)檢查所有文件。

          ts-loader 提供了一個(gè) transpileOnly 選項(xiàng),它默認(rèn)為 false,我們可以把它設(shè)置為 true,這樣項(xiàng)目編譯時(shí)就不會進(jìn)行類型檢查,也不會輸出聲明文件。

          對一下 transpileOnly 分別設(shè)置 falsetrue 的項(xiàng)目構(gòu)建速度對比:

          • 當(dāng) transpileOnly 為 false 時(shí),整體構(gòu)建時(shí)間為 4.88s.
          • 當(dāng) transpileOnly 為 true 時(shí),整體構(gòu)建時(shí)間為 2.40s.

          雖然構(gòu)建速度提升了,但是有了一個(gè)弊端: 打包編譯不會進(jìn)行類型檢查

          好在官方推薦了這樣一個(gè)插件, 提供了這樣的能力:fork-ts-checker-webpack-plugin

          官方示例的使用也非常簡單:

          const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')

          module.exports = {
            ...
            plugins: [
              new ForkTsCheckerWebpackPlugin()
            ]
          }

          在我這個(gè)實(shí)際的項(xiàng)目中,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));
          }

          修改之后, 構(gòu)建就成功了。

          關(guān)于Node垃圾回收

          在 Node.js 里面,V8 自動幫助我們進(jìn)行垃圾回收, 讓我們簡單看一下V8中如何處理內(nèi)存。

          一些定義

          • 常駐集大?。菏荝AM中保存的進(jìn)程所占用的內(nèi)存部分,其中包括:
            1. 代碼本身
          • stack:包含原始類型和對對象的引用
          • 堆:存儲引用類型,例如對象,字符串或閉包
          • 對象的淺層大小:對象本身持有的內(nèi)存大小
          • 對象的保留大?。簞h除對象及其相關(guān)對象后釋放的內(nèi)存大小

          垃圾收集器如何工作

          垃圾回收是回收由應(yīng)用程序不再使用的對象所占用的內(nèi)存的過程。

          通常,內(nèi)存分配很便宜,而內(nèi)存池用完時(shí)收集起來很昂貴。

          如果無法從根節(jié)點(diǎn)訪問對象,則該對象是垃圾回收的候選對象,因此該對象不會被根對象或任何其他活動對象引用。

          根對象可以是全局對象,DOM元素或局部變量。

          堆有兩個(gè)主要組成部分,即 New SpaceOld Space

          新空間是進(jìn)行新分配的地方。

          在這里收集垃圾的速度很快,大小約為1-8MB。

          留存在新空間中的物體被稱為新生代。

          在新空間中幸存下來的物體被提升的舊空間-它們被稱為老生代。

          舊空間中的分配速度很快,但是收集費(fèi)用很高,因此很少執(zhí)行。

          Node 垃圾回收

          Why is garbage collection expensive?

          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引擎使用兩種不同的收集算法

          1. Scavenge: 速度很快,可在新生代上運(yùn)行,
          2. Mark-Sweep: 速度較慢,并且可以在老生代上運(yùn)行。

          篇幅有限,關(guān)于v8垃圾回收的更多信息,可以參考如下文章:

          1. http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection
          2. https://juejin.cn/post/6844903878928891911
          3. https://juejin.cn/post/6844903859089866760

          總結(jié)

          小小總結(jié)一下,上文介紹了兩種方式:

          1. 直接加大內(nèi)存,使用: node --max-old-space-size=4096
          2. 把一些耗內(nèi)存進(jìn)程獨(dú)立出去, 使用了一個(gè)插件: fork-ts-checker-webpack-plugin

          希望大家留個(gè)印象, 記得這兩種方式。

          好了, 內(nèi)容就這么多, 謝謝。

          相關(guān)資料

          1. https://www.cloudbees.com/blog/understanding-garbage-collection-in-node-js/
          2. http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection
          3. https://blog.risingstack.com/finding-a-memory-leak-in-node-js/


             “分享、點(diǎn)贊在看” 支持一波  


          瀏覽 146
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  青青草免费公开视频 | 做爱污污短视屏在线观看 | 黑人综合网 | 日韩无码黄色片 | 1级毛片网站 |