12-1 内存泄漏

Posted by CodingWithAlice on July 3, 2021

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、被遗忘的闭包

内部函数调用外部函数的变量,但是内部函数没有被执行,那么闭包存储的变量在内存中就一直占用着