<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

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

    共 6914字,需瀏覽 14分鐘

     ·

    2021-05-12 22:16

    背景

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

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

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

    正文

    報錯示意圖:

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

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

    但 Node 進程的內(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.

    翻譯一下:

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

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

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

    運行如下代碼:

    // 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 左右內(nèi)存時候拋出異常。

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

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

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

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

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

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

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

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

    當項目中的代碼變的越來越多,體積也越來越龐大時,項目編譯時間也隨之增加。

    這是因為 Typescript 的語義檢查器必須在每次重建時檢查所有文件

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

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

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

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

    好在官方推薦了這樣一個插件, 提供了這樣的能力: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));
    }

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

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

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

    一些定義

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

    垃圾收集器如何工作

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

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

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

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

    堆有兩個主要組成部分,即 New SpaceOld Space。

    新空間是進行新分配的地方。

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

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

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

    舊空間中的分配速度很快,但是收集費用很高,因此很少執(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: 速度很快,可在新生代上運行,
    2. Mark-Sweep: 速度較慢,并且可以在老生代上運行。

    篇幅有限,關(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)存進程獨立出去, 使用了一個插件: fork-ts-checker-webpack-plugin

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

    好了, 內(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/


       “分享、點贊、在看” 支持一波  


    瀏覽 146
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報
    評論
    圖片
    表情
    推薦
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    亚洲伊人成人 | 国产精品内射婷婷二级一 | 嗯~进去~好大~好满女攻视频 | 亚洲免费电影黄 | 97操操操 |