12-1 内存泄漏
参考文章:深入了解内存泄漏
核心要点记录:
-
Chrome 工具:内存 Memory 录制监测
- 内存泄漏:如果这段数据已经不再需要了,但是又没有销毁。
- 几种垃圾回收机制:详情看上一篇 垃圾回收机制,标记-清除、标记-整理等算法
-
如何避免内存泄漏:
- 1、尽可能少地创建全局变量、少用闭包
- 2、手动清除定时器:clearInterval/clearTimeout
- 3、手动移除事件监听器:removeEventListener、customEvent.off
- 4、使用弱引用或者手动删除 Set 成员、Map 中的键名:WeakSet WeakMap set.delete(value) map.delete(key)
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 set = new Set();
let value = { test: 22};
set.add(value);
value= null;
// 以上写法是有内存泄漏的,需要添加如下代码
set.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、被遗忘的闭包
内部函数调用外部函数的变量,但是内部函数没有被执行,那么闭包存储的变量在内存中就一直占用着