JavaScript垃圾回收方法

119 阅读3分钟

JavaScript垃圾回收方法

JavaScript 的垃圾回收机制(Garbage Collection, GC)自动管理内存,开发者无需手动分配和释放内存。其核心方法如下:

1. 主要垃圾回收算法

(1) 标记-清除(Mark-and-Sweep)

  • 原理:从根对象(如全局变量、活动函数调用栈)出发,标记所有可达对象,清除未标记的对象。
  • 优点:解决循环引用问题。
  • 流程
    1. 标记阶段:遍历对象图,标记所有可达对象。
    2. 清除阶段:回收未被标记的内存。
  • 示例
    let objA = { ref: null };
    let objB = { ref: null };
    objA.ref = objB;
    objB.ref = objA;
    // 即使 objA 和 objB 互相引用,若无法从根访问,仍会被回收。
    

(2) 引用计数(已淘汰)

  • 原理:记录每个对象的引用次数,归零时回收。
  • 缺点:无法处理循环引用。
    function createCycle() {
      let x = {};
      let y = {};
      x.ref = y;
      y.ref = x; // 循环引用,引用计数无法归零。
    }
    

2. 分代回收(Generational Collection)

现代引擎(如 V8)将对象分为两代,针对不同生命周期优化回收效率。

(1) 新生代(Young Generation)

  • 特点:存放短期存活对象(如局部变量)。
  • 算法Scavenge 算法(复制算法)。
    • 将内存分为 FromTo 两个半空间。
    • 存活对象从 From 复制到 To,然后清空 From
    • 晋升:多次存活的对象移至老生代。

(2) 老生代(Old Generation)

  • 特点:存放长期存活对象(如全局变量)。
  • 算法标记-清除 + 标记-整理(减少内存碎片)。

3. 优化策略

(1) 增量标记(Incremental Marking)

  • 原理:将标记过程拆分为多段,穿插在主线程任务中执行,减少长时间停顿。
  • 适用场景:大型应用避免界面卡顿。

(2) 空闲时间回收(Idle-time GC)

  • 原理:在浏览器空闲时段触发垃圾回收。
  • 实现:通过 requestIdleCallback 调度。

(3) 并行/并发回收

  • 并行:多个辅助线程同时执行垃圾回收。
  • 并发:主线程运行应用逻辑,辅助线程执行回收。

4. V8 引擎的垃圾回收

  • 新生代:使用 Scavenge 算法,快速回收短期对象。
  • 老生代:组合使用标记-清除(回收垃圾)和标记-整理(整理内存碎片)。
  • 全停顿(Stop-The-World):老生代回收时可能短暂阻塞主线程。

5. 开发者注意事项

(1) 避免内存泄漏

  • 意外全局变量

    function leak() {
      leakedVar = 'This is a global'; // 未用 let/const/var 声明!
    }
    
  • 未清理的引用

    • 定时器、事件监听器、闭包保留的 DOM 引用。
    // 错误示例
    let element = document.getElementById('button');
    element.addEventListener('click', onClick);
    // 若 element 移除后未取消监听,回调函数仍被引用。
    

(2) 手动解除引用

  • 不再使用的对象设为 null,加速回收:
    let data = loadHugeData();
    // 使用完毕后
    data = null;
    

(3) 使用内存分析工具

  • Chrome DevTools
    • Memory 面板:生成堆快照(Heap Snapshot),对比内存变化。
    • Performance 面板:监控内存分配趋势。
  • Node.js:使用 --inspect 标志和 Chrome DevTools 调试。

6. 示例:内存泄漏检测

// 模拟内存泄漏(未清理的数组)
let leaks = [];
setInterval(() => {
  leaks.push(new Array(1000000).fill('*'));
}, 1000);

// 在 Chrome DevTools 的 Memory 面板中:
// 1. 拍摄堆快照。
// 2. 多次操作后拍摄第二个快照。
// 3. 对比快照,查找未被回收的数组。

总结

机制核心方法场景优化手段
新生代回收Scavenge 算法(复制)短期存活对象快速清理,晋升长期对象
老生代回收标记-清除 + 标记-整理长期存活对象减少碎片,增量标记
开发者要点避免循环引用、及时解除引用防止内存泄漏,提升性能使用工具分析,合理设计代码

理解垃圾回收机制有助于编写高效、稳定的 JavaScript 代码,避免内存问题导致的性能下降或崩溃。

更多vue相关插件及后台管理模板可访问vue admin reference,代码详情请访问github