Skip to main content

内存管理与垃圾回收 (GC)

在高级 Node.js 面试中,内存泄漏的排查和 V8 的内存管理机制是必考的底层知识。

V8 内存结构

Node.js 程序运行时的内存主要分为以下两部分:

  1. V8 堆内存 (Heap)

    • V8 引擎分配的内存,受限于 V8 的内存大小限制(64位系统默认约 1.4GB,32位系统默认约 0.7GB)。
    • 所有的 JavaScript 对象、闭包等都存储在这里。
    • 分为新生代 (New Space)老生代 (Old Space)
  2. 堆外内存 (Off-Heap / External Memory)

    • 不受 V8 引擎内存大小限制的内存。
    • Buffer 对象、C++ 层面分配的内存(如网络套接字、底层的 I/O 缓冲区)都属于堆外内存。
    • 这是 Node.js 处理大文件时(如视频切片流)避免内存溢出的关键。

垃圾回收机制 (Garbage Collection)

V8 采用分代式垃圾回收机制

1. 新生代 (New Space)

  • 特点:存放存活时间短的对象,内存空间较小(通常在 16MB ~ 32MB 左右)。
  • 算法:采用 Scavenge 算法(基于 Cheney 算法)。
    • 将新生代内存分为两半:From 空间和 To 空间。
    • 垃圾回收时,检查 From 空间中的存活对象,并将它们复制到 To 空间中,然后清空 From 空间。
    • 最后,FromTo 空间角色互换。
  • 晋升机制:如果一个对象经历了多次 Scavenge 回收依然存活,或者 To 空间占比超过 25%,该对象会被晋升到老生代。

2. 老生代 (Old Space)

  • 特点:存放存活时间长或体积大的对象,占据了 V8 绝大部分内存。
  • 算法:采用 Mark-Sweep (标记清除) 和 Mark-Compact (标记整理)。
    • Mark-Sweep:遍历堆内存,标记所有存活的对象。然后清理掉没有被标记的对象。这会产生内存碎片。
    • Mark-Compact:在空间不足以分配大对象时触发。将所有存活的对象往内存的一端移动,清理掉边界外的内存,解决内存碎片问题。

3. Orinoco 优化机制 (面试加分项)

为了避免垃圾回收时长时间暂停主线程(Stop-The-World),V8 引入了以下优化机制:

  • 增量标记 (Incremental Marking):将标记过程分成小段,与 JS 主线程交替执行。
  • 并发清理 (Concurrent Sweeping):在后台线程同时进行清理工作,不阻塞主线程。

Node.js 内存泄漏排查

内存泄漏是指本该被垃圾回收的对象,因为某些原因仍然被其他存活对象引用,导致无法被释放。

常见的内存泄漏场景

  1. 全局变量滥用:挂载在 global 上的大对象(如未被清除的数组或 Map)。
  2. 闭包引起的内存泄漏:长生命周期的闭包中持有了巨大的外部作用域变量引用(比如在请求生命周期中定义了全局函数并持有 req / res 的引用)。
  3. 缓存未设置上限:在内存中实现简单的 Map 缓存,但没有设置 LRU(最近最少使用)淘汰策略或最大容量,导致缓存无限增长。生产环境强烈建议使用 Redis。
  4. 事件监听器未移除eventEmitter.on() 绑定了事件但没有在适当的时机 removeListener(),导致监听器数组无限膨胀(Node.js 默认同一个事件超过 10 个监听器会抛出警告)。

高级排查工具与手段

  1. process.memoryUsage()
    • 可以在应用中写一个定时器打印该方法的结果,观察 heapUsed(已使用的堆内存)是否随时间呈阶梯状持续上升,这是判断内存泄漏最基础的指标。
  2. heapdump / v8 模块打快照
    • 使用 v8.writeHeapSnapshot() 生成内存快照文件(.heapsnapshot)。
    • 通常需要打两个快照:一个在内存平稳时,一个在内存飙升时。
  3. Chrome DevTools (Memory 面板)
    • 将生成的 .heapsnapshot 文件导入 Chrome 浏览器的 DevTools -> Memory 面板。
    • 使用 Comparison(对比) 视图,对比两个快照,查看增加最多的对象类型,进而定位到泄露的具体代码位置(Retainers 视图可以查看是谁持有了它的引用)。
  4. Clinic.js (性能诊断利器)
    • clinic doctor:生成直观的 CPU 和内存占用图表。
    • clinic flame:生成火焰图,定位 CPU 密集型的 Long Task 阻塞。