• 技术文章 >web前端 >js教程

    聊聊Node.js中的 GC (垃圾回收)机制

    青灯夜游青灯夜游2022-11-29 20:44:08转载156
    Node 是如何做 GC (垃圾回收)的?下面本篇文章就来带大家了解一下。

    大前端成长进阶课程:进入学习

    GC,Garbage Collection,垃圾回收。在编程中,一般指的是内存自动回收机制,会定时将不需要用到的数据进行清除。

    Node.js 底层使用了 V8 引擎。V8 是 Google 开源的一款高性能 JavaScript 引擎,使用了 C++ 进行编写。【相关教程推荐:nodejs视频教程

    Node.js 的内存主要分成三部分:

    堆内存

    Node.js 底层使用的是 V8,下面讲解一下 V8 的内存回收机制。

    首先 JS 中所有的对象都会保存在堆内存中。在创建进程的时候,会分配一个初始大小的堆内存,然后我们的对象就会放到里面。

    当对象越来越多,堆内存会不够用,此时堆内存会动态地扩大。如果到达一个最大限制(现在通常是 4GB),就会堆内存溢出的错误,然后终止 Node.js 进程。

    新生代与老生代

    V8 首先将内存分成两部分,或者说两个生代(generation):

    新生代很小,这里会存放一些存活时间很短的对象,通常它们会被频繁地回收(比如函数的调用栈的一些临时对象)。

    新生代可通过 node --max-semi-space-size=SIZE index.js 修改新生代的大小,单位为 MB。

    另外,老生代则通过 --max-old-space-size=SIZE 来设置

    新生代的 Scavenge 算法

    新生代使用了 Scavenge 算法,是一种基于 copy(复制)的算法。

    新生代会分成两个空间,这种空间称为 semispace,它们为:

    新声明的对象会放入到 From 空间中,From 空间的对象紧密排布,通过指针,上一对象紧贴下一个对象,是内存连续的,不用担心内存碎片的问题。

    所谓内存碎片,指的是空间分配不均匀,产生大量小的连续空间,无法放入一个大对象。

    当 From 空间快满了,我们就会遍历找出活跃对象,将它们 copy 到 To 空间。此时 From 空间其实就空了,然后我们将 From 和 To 互换身份。

    如果一些对象被 copy 了多次,会被认为存活时间较长,将被移动到老生代中。

    这种基于 copy 的算法,优点是可以很好地处理内存碎片的问题,缺点是会浪费一些空间作为搬移的空间位置,此外因为拷贝比较耗费时间,所以不适合分配太大的内存空间,更多是做一种辅助 GC。

    Mark-Sweep 和 Mark-Compact

    老生代的空间就比新生代要大得多了,放的是一些存活时间长的对象,用的是 Mark-Sweep (标记清除)算法。

    首先是标记阶段。从根集 Root Set(执行栈和全局对象)往上找到所有能访问到的对象,给它们标记为活跃对象。

    标记完后,就是清除阶段,将没有标记的对象清除,其实就是标记一下这个内存地址为空闲。

    这种做法会导致 空闲内存空间碎片化,当我们创建了一个大的连续对象,就会找不到地方放下。这时候,就要用 Mark-Compact(标记整理)来将碎片的活跃对象做一个整合。

    Mark-Compact 会将所有活跃对象拷贝移动到一端,然后边界的另一边就是一整块的连续可用内存了。

    考虑到 Mark-Sweep 和 Mark-Compact 花费的时间很长,且会阻塞 JavaScript 的线程,所以通常我们不会一次性做完,而是用 增量标记 (Incremental Marking)的方式。也就是做断断续续地标记,小步走,垃圾回收和应用逻辑交替进行。

    另外,V8 还做了并行标记和并行清理,提高执行效率。

    图片

    查看内存相关信息

    我们可以通过 process.memoryUsage 方法拿到内存相关的一些信息。

    process.memoryUsage();

    输出内容为:

    {
      rss: 35454976,
      heapTotal: 7127040,
      heapUsed: 5287088,
      external: 958852,
      arrayBuffers: 11314
    }

    说明

    以上数字的单位都是字节。

    测试最大内存限制

    写一个脚本,用一个定时器,让一个数组不停地变大,并打印堆内存使用情况,直到内存溢出。

    const format = function (bytes) {
      return (bytes / 1024 / 1024).toFixed(2) + " MB";
    };
    
    const printMemoryUsage = function () {
      const memoryUsage = process.memoryUsage();
      console.log(
        `heapTotal: ${format(memoryUsage.heapTotal)}, heapUsed: ${format(
          memoryUsage.heapUsed
        )}`
      );
    };
    
    const bigArray = [];
    setInterval(function () {
      bigArray.push(new Array(20 * 1024 * 1024));
      printMemoryUsage();
    }, 500);

    需要特别注意的是,不要用 Buffer 做测试。

    因为 Buffer 是 Node.js 特有的处理二进制的对象,它不是在 V8 中的实现的,是 Node.js 用 C++ 另外实现的,不通过 V8 分配内存,属于堆外内存。

    我使用电脑是 macbook pro M1 Pro,Node.js 版本为 v16.17.0,使用的 V8 版本是 9.4.146.26-node.22(通过 process.versions.v8 得到)。

    输出结果为(省略了一些多余的信息):

    heapTotal: 164.81 MB, heapUsed: 163.93 MB
    heapTotal: 325.83 MB, heapUsed: 323.79 MB
    heapTotal: 488.59 MB, heapUsed: 483.84 MB
    ...
    heapTotal: 4036.44 MB, heapUsed: 4003.37 MB
    heapTotal: 4196.45 MB, heapUsed: 4163.29 MB
    
    <--- Last few GCs --->
    
    [28033:0x140008000]    17968 ms: Mark-sweep 4003.2 (4036.4) -> 4003.1 (4036.4) MB, 2233.8 / 0.0 ms  (average mu = 0.565, current mu = 0.310) allocation failure scavenge might not succeed
    [28033:0x140008000]    19815 ms: Mark-sweep 4163.3 (4196.5) -> 4163.1 (4196.5) MB, 1780.3 / 0.0 ms  (average mu = 0.413, current mu = 0.036) allocation failure scavenge might not succeed
    
    
    <--- JS stacktrace --->
    
    FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
    ...

    可以看到,是在 4000 MB 之后超出了内存上限,发生堆溢出,然后退出了进程。说明在我的机器上,默认的最大内存为 4G。

    实际最大内存和它运行所在的机器有关,如果你的机器的内存大小为 2G,最大内存将设置为 1.5G。

    更多node相关知识,请访问:nodejs 教程

    以上就是聊聊Node.js中的 GC (垃圾回收)机制的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除

    前端(VUE)零基础到就业课程:点击学习

    清晰的学习路线+老师随时辅导答疑

    自己动手写 PHP MVC 框架:点击学习

    快速了解MVC架构、了解框架底层运行原理

    专题推荐:前端 Node.js
    上一篇:聊聊怎么使用Node将Excel转为JSON 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • ❤️‍🔥共22门课程,总价3725元,会员免费学• ❤️‍🔥接口自动化测试不想写代码?• win环境下node版本怎么切换?(升级降级)• 什么是模块化?聊聊Node模块化的那些事• 详解node中的包和包管理工具• nodejs可视化学习:事件循环【动图演示】• 聊聊怎么使用Node将Excel转为JSON
    1/1

    PHP中文网