Node.js:V8的垃圾回收原理

218 阅读2分钟

原文链接:NodeJs internals: V8 & garbage collector

  • 应用出现 heap out of memory 的时候
    • 使用 --trace_gc 来追踪垃圾回收
    • 使用 process.memoryUsage 查看 heapUsed, heapTotal
    • v8-profiler
    • node-heapdump
    • node-memwatch
  • Node.js(V8) 的 GC 机制
    • 堆内存中分为 [[new-space][old-space]]
      • 新生代内存空间:保存存活时间较短的对象
      • 老生代内存空间:保存存活时间较长或常驻内存的对象
    • Scavenge 算法:新生代对象的垃圾回收算法
      • 采用复制的方式。
      • 堆内存一分为二,两个部分成为 semisapce
        • From空间:使用中的semispace
        • To空间:闲置中的semispace
      • 分配内存的时候,先从From空间中分配。
      • 垃圾回收的时候,检查From空间的存活对象,复制这些存活对象到To空间,非存活对象的空间释放。
      • 复制完成之后,From空间和To空间角色对换。
      • 新生代中存活的对象会转移空间到老生代中,被称为晋升,条件为:
        • 对象经历过一次 Scavenge 回收
        • To 空间使用超过25%
    • Mark-Sweep:老生代对象的垃圾回收算法
      • 标记清除,分为两个阶段
        • 标记阶段:遍历堆中的所有对象,标记活着的对象
        • 清除阶段:清除未被标记的对象
      • 每一次清除标记之后会产生内存空间不连续的情况,内存碎片。
        • 如果需要分配内存给一个大的对象,就有可能导致所有的碎片空间都无法完成分配,而触发垃圾回收。
    • Mark-Compact:在Mark-Sweep基础上优化内存碎片问题
      • 标记阶段结束后,把所有活着对象都往一端移动
      • 移动完成之后直接清理掉另一边的所有内存
    • Stop-the-world:GC需要暂停你的JavaScript执行代码来完成垃圾回收,V8的解决方案:
      • Incremental-Marking:增量标记,把GC的一个需要长时间标记的任务分成多次来完成
      • Lazy-Sweeping:懒清除,当前的空闲内存足以运行当前的代码的时候就先不清除
      • Concurrent:V8利用多个线程(Worker)来同时进行标记清除
      • Parallel:在JavaScript代码执行的时候,GC与其平行进行标记清除,以避免 Stop-the-world