12-1 内存泄漏
参考文章:深入了解内存泄漏
核心要点记录:
-
内存泄漏:如果这段数据已经不再需要了,但是又没有销毁。
-
几种垃圾回收机制:详情看上一篇 垃圾回收机制,标记-清除、标记-整理等算法
-
如何避免内存泄漏:
- 1 尽可能少地创建全局变量
- 2 手动清除定时器
- 3 少用闭包
- 4 清除DOM引用
- 5 弱引用
-
JS 内存泄露的常见场景
-
1、意外的全局变量(eslint 校验会报错)
// 在全局作用域下定义 function count(number) { // basicCount 相当于 window.basicCount = 2; basicCount = 2; return basicCount + number; }
-
2、被遗忘的计时器
如果在
Vue
的一个组件中使用了计时器,组件销毁 的时候,setInterval
还是在运行的,里面涉及到的内存都是没法回收的(浏览器会认为这是必须的内存,不是垃圾内存)mounted() { this.refreshInterval = setInterval(function() { // 轮询获取数据 this.refresh() }, 2000) }, // 销毁前清除计时器 beforeDestroy() { clearInterval(this.refreshInterval) },
-
3、被遗忘的事件监听器
组件销毁的时候,
resize
事件还是在监听中,里面涉及到的内存都是没法回收的(浏览器会认为这是必须的内存,不是垃圾内存),mounted() { this.resizeEventCallback = () => { // 这里做一些操作 } window.addEventListener('resize', this.resizeEventCallback) }, // 销毁前移除监听事件 beforeDestroy() { window.removeEventListener('resize', this.resizeEventCallback) },
-
4、被遗忘的 ES6 Set 成员
对
Set
处理map.delete(value)
,或者使用 弱引用(内存回收不会考虑到这个引用是否存在)WeakSet
let map = new Set(); let value = { test: 22}; map.add(value); value= null; // 以上写法是有内存泄漏的,需要添加如下代码 map.delete(value);
-
5、被遗忘的 ES6 Map 键名
对
Map
处理map.delete(key)
,或者使用WeakMap
,WeakMap 的键名是弱引用,内存回收不会考虑到这个引用是否存在。let map = new Map(); let key = new Array(5 * 1024 * 1024); map.set(key, 1); key = null; // 以上写法是有内存泄漏的,需要添加如下代码 map.delete(key);
-
6、被遗忘的订阅发布事件监听器
import customEvent from 'event' export default { methods: { onClick() { customEvent.emit('test', { type: 'click' }) }, }, mounted() { customEvent.on('test', data => { // 一些逻辑 console.log(data) }) }, // 销毁组件前 off 监听事件 beforeDestroy() { customEvent.off('test') }, }
-
7、被遗忘的闭包
内部函数调用外部函数的变量,但是内部函数没有被执行,那么闭包存储的变量在内存中就一直占用着
-