JS 的 GC 机制和内存泄露

引用计数(reference counting)
在早期的 IE 浏览器(Netscape Navigator 3.0 采用)中,JavaScript 使用的是引用计数垃圾回收机制。这种 GC 机制在循环引用的场景下很可能造成内存泄漏问题。
内存泄漏(Memory Leak)
内存泄漏指程序在申请内存后,无法释放已申请的内存空间,导致系统可用内存逐渐减少的现象。
核心思想:
- 变量只是对值进行引用
- 当变量引用该值时,引用次数+1
- 当该变量的引用被覆盖或者清除时,引用次数-1
- 当引用次数为0时,就可以安全地释放这块内存。
1 | let arr = [1, 0, 1] // [1, 0, 1]这块内存被arr引用 引用次数为1 |
内存泄露一般发生在有复杂数据类型或者函数的情况里。
下面是一段循环引用的示例,这段代码在早期 IE 里会导致内存泄露。
1 | function Example(){ |
- 解决方法:在函数结束时将其指向null
1 | ObjectA = null; |
标记清除(mark-and-sweep)
为了解决循环引用造成的内存泄漏问题,Netscape Navigator 4.0 开始采用标记清除法
到了 2008 年,IE、Firefox、Opera、Chrome 和 Safari 都在自己的 JavaScript 实现中采用标记清理(或 其变体),只是在运行垃圾回收的频率上有所差异。
它的核心流程分为两个阶段:
-
标记(mark):从根对象(root)开始,递归地标记所有可以到达的对象。
-
清除(sweep):遍历堆中的所有对象,回收那些没有被标记的对象。
这样,即使有循环引用(对象之间互相引用),只要它们无法从根对象访问到,它们就会被清理掉。
常见的内存泄露案例
1. 对象间循环引用
1 | let parent = { children: [] }; |
2. 闭包引用
详情可见 JS 闭包 (Closure)
1 | function createClosure() { |
3. DOM 事件监听器未清理
1 | let element = document.getElementById('button'); |
4. 定时器未清理
1 | let data = { /* 大量数据 */ }; |
如何检测内存泄漏
这个其实不难,浏览器原带的开发者工具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 进行许可。
评论