12-1 内存泄漏

Posted by CodingWithAlice on July 3, 2021

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

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