JS 的 GC 机制和内存泄露

三葉Leaves Author

引用计数(reference counting)

在早期的 IE 浏览器(Netscape Navigator 3.0 采用)中,JavaScript 使用的是引用计数垃圾回收机制。这种 GC 机制在循环引用的场景下很可能造成内存泄漏问题。

内存泄漏(Memory Leak)

内存泄漏指程序在申请内存后,无法释放已申请的内存空间,导致系统可用内存逐渐减少的现象。

核心思想:

  • 变量只是对值进行引用
  • 当变量引用该值时,引用次数+1
  • 当该变量的引用被覆盖或者清除时,引用次数-1
  • 当引用次数为0时,就可以安全地释放这块内存。
1
2
3
4
let arr = [1, 0, 1]   // [1, 0, 1]这块内存被arr引用  引用次数为1
arr = [0, 1, 0] // [1, 0, 1]的内存引用次数为0被释放
// [0, 1, 0]的内存被arr引用 引用次数为1
const tmp = arr // [0, 1, 0]的内存被tmp引用 引用次数为2

内存泄露一般发生在有复杂数据类型或者函数的情况里。

下面是一段循环引用的示例,这段代码在早期 IE 里会导致内存泄露。

1
2
3
4
5
6
7
8
9
10
11
function Example(){

let ObjectA = new Object();
let ObjectB = new Object();

ObjectA.p = ObjectB;
ObjectB.p = ObjectA;

}

Example();
  • 解决方法:在函数结束时将其指向null
1
2
ObjectA = null;
ObjectB = null;

标记清除(mark-and-sweep)

为了解决循环引用造成的内存泄漏问题,Netscape Navigator 4.0 开始采用标记清除法
到了 2008 年,IE、Firefox、Opera、Chrome 和 Safari 都在自己的 JavaScript 实现中采用标记清理(或 其变体),只是在运行垃圾回收的频率上有所差异。

它的核心流程分为两个阶段:

  1. 标记(mark):从根对象(root)开始,递归地标记所有可以到达的对象。

  2. 清除(sweep):遍历堆中的所有对象,回收那些没有被标记的对象。

这样,即使有循环引用(对象之间互相引用),只要它们无法从根对象访问到,它们就会被清理掉。

常见的内存泄露案例

1. 对象间循环引用

1
2
3
let parent = { children: [] };
let child = { parent: parent };
parent.children.push(child);

2. 闭包引用

详情可见 JS 闭包 (Closure)

1
2
3
4
5
6
function createClosure() {
let largeData = new Array(1000000).fill('data');
return function() {
console.log(largeData.length); // 闭包持有 largeData 引用
};
}

3. DOM 事件监听器未清理

1
2
3
4
5
6
let element = document.getElementById('button');
let data = { /* 大量数据 */ };
element.addEventListener('click', function() {
console.log(data); // 事件处理函数持有 data 引用
});
// 忘记 removeEventListener

4. 定时器未清理

1
2
3
4
5
let data = { /* 大量数据 */ };
let timer = setInterval(function() {
console.log(data);
}, 1000);
// 忘记 clearInterval(timer)

如何检测内存泄漏

这个其实不难,浏览器原带的开发者工具Performance就可以

  • 步骤
    • F12打开开发者工具
    • 选择Performance工具栏
    • 勾选屏幕截图和Memory
    • 点击开始录制
    • 一段时间之后结束录制
  • 结果
    • 堆内存会周期性地分配和释放
    • 如果堆内存的min值在逐渐上升则存在内存泄漏
  • 标题: JS 的 GC 机制和内存泄露
  • 作者: 三葉Leaves
  • 创建于 : 2025-06-01 00:00:00
  • 更新于 : 2025-06-07 19:46:13
  • 链接: https://blog.oksanye.com/318bbddda306/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论