美文网首页
《深入浅出Node.js》内存控制

《深入浅出Node.js》内存控制

作者: 我叫Aliya但是被占用了 | 来源:发表于2022-02-27 18:29 被阅读0次

依托 V8 的 NODE 在使用内存时是有大小限制的,具体大小与系统类型、版本、NODE 版本相关(64 位系统 1.4GB 和 32 位系统 0.7 GB 的大小)。

NODE 与 C++不同,垃圾回收 GC 由系统自动执行,不由开发者参与,当代码使用不当时,会导致 GC 不能正确回收发生内存泄漏

V8 对象(内存)分配 及回收

> node --max-old-space-size=1700 test.js // 单位为MB,老生代内存大小限制
> node --max-new-space-size=1024 test.js // 单位为KB,新生代内存大小限制
>
> process.memoryUsage() // 查看内存使用情况
< {
    rss: 14958592,
    heapTotal: 7195904, // 申请到的堆内存
    heapUsed: 2821496   // 使用的量
}
  • V8 内存分代:新生代中的对象存活时间较短;老生代中的对象长驻或常驻内存;

    • 老生代在 64 位系统下默认大小限制为 1400 MB,在 32 位 700 MB
    • 老生代在 32 位系统下默认大小限制为 32 MB,在 32 位 16 MB
  • 回收算法:Scavenge 算法(新生代)、Mark-Sweep & Mark-Compact(老生代两者结合使用)

    • Scavenge:将堆内存一分为二,二者只有一个处理使用状态,称为 From 空间,空闲的为 To 空间。
      • 分配对象在 From 空间。
      • 垃圾回收时,释放 From 中非活对象,复制存活对象到 To 空间。
      • 完成复制后两个空间的角色发生对换(又称翻转)。
      • 晋升:当一个对象多次复制后依然存活,则移动到老生代中;或 To 空间使用超过 25%。
    • Mark-Sweep:标记活着的对象,回收没有标记的对象。(会造成空间不连续)
    • Mark-Compact:将活着的对象往一端移动,移动完成后,清理掉边界外的内存。
  • 回收执行时机

    • 全停顿 stop-the-world:GC 执行时需要将应用暂停,完成 GC 再恢复(时间代价较大)
    • 增量标记 incremental marking:(老生代)从标记阶段入手,拆分为小步,逻辑执行与 GC 交替执行
    • 后续还引入了延迟清理(lazy sweeping)与增量式整理(incremental compaction),让清理与整理动作也变成增量式的,进一步利用多核性能。
  • GC 耗时分析:Node 启动时使用--prof 参数,可以得到 V8 执行时的性能分析数据。但得到的日志文件不具备可读性,需要借助工具(linux-tick-processor、windows-tick-processor.bat)linux-tick-processor v8.log来分析 GC 的耗时

作用域和闭包对内存的影响

  • 形成作用域:函数调用、with、全局作用域

    • 局部变量分配在作用域空间上,随作用域回收而释放。如果变量小周期短会被分配到新生代的 Form 空间。
    • 作用域链:在当前作用域中无法找到变量的声明,将会向上级的作用域里查找,直到查到为止。
    • 变量的主动释放:=undefined 或 delete 对象。在 node10 中,对象和计算量多的情况下,执行 =undefined 比 delete 大部分时间是快的,在 chrome90 中更明显。
  • 闭包:外部作用域访问内部作用域中变量的方法叫做闭包(closure)

    • 比如 A 方法返回 B 函数,B 访问 A 中变量,a = A(),只要 a 不被释放 B 占用的内存就得不到释放。(a 不使用了及时=undefined,以释放 B)

查看内存使用情况

> process.memoryUsage() // 查看内存使用情况
< {
    rss: 14958592,      // 常驻集大小,包括所有 C++ 和 JavaScript 对象和代码;
    heapTotal: 7195904, // 申请到的堆内存
    heapUsed: 2821496,  // 使用的量
    external: 13522,    // 绑定到 V8 管理的 JavaScript 对象的 C++ 对象的内存使用量。
    arrayBuffers: 15159 // 所有 Node.js Buffer
}
> os.totalmem() // 系统的总内存
< 8589934592
> os.freemem()  // 系统闲置内存
< 185921536
  • rss: resident set size 常驻集大小
  • external:外部的,堆外内存

heapTotal 不包含 rss、arrayBuffers、external。external 包含 arrayBuffers。批量操作 buffer 后,heapTotal、heapUsed 不变,arrayBuffers、external 变大(rss 短时涨一些,很快就下去了)

内存泄漏

  • 常见原因:意外的全局变量、没有及时清理的计时器或回调、闭包

  • 慎将内存当做缓存,比如 store 中的大对象、已经不用的变量,导致内存泄漏甚至溢出。存储需求强烈可以使用 Redis 或 indexdb 等不占用 v8 缓存限制的方式。

  • 日志收集时,写入操作慢于日志产生,js 队列数据过大导致内存溢出,或记录日志的 js 相关作用域得不到释放,出现内存泄漏。

内存泄漏排查工具

  • v8-profiler,可以用来分析 cpu(书中未详说)

  • node-heapdump:npm i heapdump;在代码的第一行添加如下代码将其引入;kill -USR2 PID生成分析文件;导入 chrome 中的 Profiles 中进行分析,根据新生代(shallow size)、老生代(retained size)内存占比推测泄漏的数据

  • node-memwatch:npm i memwatch;通过改变 heapDiff 的开始位置,或许可以逐步定位泄漏的位置,通过 diff 结果可以推测泄漏的为数组

    memwatch.on("stats", cb)  // 全堆垃圾回收
    memwatch.on("leak", cb)  // 内存泄漏(如连续5次垃圾回收内存仍未释放)
    var hd = new memwatch.HeapDiff();
    /* 要分析的代码 */
    ...
    
    var diff = hd.end(); // 内容差异结果
    /*
    {
      what: "String",
      size_bytes: 879424,
      size: "858.81 kb",
      +: 20001,   // 分配的字符串对象数量
      -: 1        // 释放的字符串对象数量
    }
    */
    
  • 流(stream)或管道(pipe)专门用来操作需要大内存的数据,且不受 V8 内存限制的影响

  • 纯粹的 Buffer 操作(不涉及字符串),也不受 V8 内存限制的影响

相关文章

  • 理解 Node.js 的 GC 机制

    《深入浅出Node.js》第五章《内存控制》阅读笔记 随着 Node 的发展,JavaScript 的应用场景早已...

  • 《深入浅出Node.js》内存控制

    依托 V8 的 NODE 在使用内存时是有大小限制的,具体大小与系统类型、版本、NODE 版本相关(64 位系统 ...

  • 谈谈 Node.js 中的垃圾回收

    《深入浅出 Node.js》阅读随笔 V8 引擎下的 Javascript 在运行时,存在两部分内存空间,分别是新...

  • Node.js的内存控制

    内存限制 Node基于V8构建,所以存在一定的内存限制(64位系统下约为1.4GB, 32位系统下约为0.7GB)...

  • 2021-04-07each和foreach

    深入浅出 Node.js(三):深入 Node.js 的模块机制 https://www.infoq.cn/art...

  • 深入浅出nodejs(内存控制)

    node使用V8作为javaScript脚本引擎 v8的内存限制和对象分配 限制:64为大约1.4G,32位大约0...

  • Node.js 有什么弊端

    《深入浅出 Node.js》阅读随笔 此文为《为什么选择 Node.js》姊妹篇,简单聊一下 Node.js 的缺...

  • Node.js内存管理机制分享

    Node.js内存管理Node.js的一些选项GC研究例子生产环境的设置 1. Node.js如何管理内存 [解惑...

  • Node.js 有必要写测试用例吗?

    《深入浅出 Node.js》阅读随笔 先说结论:很有必要!不单是 Node.js,除非是作为体验或者 Demo 演...

  • 异步编程的困惑

    《深入浅出 Node.js》阅读随笔 众所周知,Node.js 虽然也有部分同步编程的方式,但主要还是以后异步编程...

网友评论

      本文标题:《深入浅出Node.js》内存控制

      本文链接:https://www.haomeiwen.com/subject/iuwhrrtx.html