js的内存泄露和内存释放问题

135 阅读3分钟

总结:在JS中,我们直到函数执行会形成私有的上下文,进行进栈执行。当执行完毕后,会有一个出栈释放的过程。正常情况下,当出栈时,上下文的内容不会被外部上下文所引用,就会将这个上下文进行消除。但是我们每次创建引用类型,无论是Object、function,都会开辟一个堆内存,浏览器会在默认空闲的时候对这些内存进行释放,以减少浏览器压力。这样的释放,我们叫做垃圾回收。当然,也存在一样无法释放的问题,这样就会导致内存泄漏。

垃圾回收

垃圾回收在不同的浏览器中,使用的方式是不同的。主要区分谷歌和IE

谷歌浏览器

  • 使用引用查找 进行回收。开辟的堆内存,在浏览器自己默认为空闲时刻,就会查找所有的内存引用,会将那些不被引用的内存进行释放。进而给浏览器减压。

IE浏览器

  • 使用计数器机制 进行回收。如果创建的内存被引用一次,那么会计数1;再被引用,计数为2;以此类推。当移除时候,每移除一次,则计数减一。直到被引用降为 0.那么浏览器会被内存释放。

内存泄漏的情况

  • 正常来说,为了降低压力,浏览器会将不被引用,或者计数次数变为0的内存进行释放。但是当遇到内存不能被释放的情况,场景多了后,就会造成 “内存泄漏

1. 循环引用导致内存泄漏

  • 当两个或多个对象之间存在相互引用,发生循环引用,导致内存无法释放

    function fn() {
        let obj1 = {}, obj2 = {};
        obj1.o = obj2;
        obj2.o = obj1;
        return obj1
    }
    let F = fn();
    
  • 上面可以看到,fn返回了 obj1,同时obj1 与 obj2 循环调用。F=obj1. 上级上下文引用 fn,导致fn无法释放。

2. 定时器

  • 在真实项目中,我们会用到定时器Interval or Timeout 。比如倒计时等情况。但是有时候没有清除这些定时器,一样会一直占用空间

    let count = 1;
    function countFn() {
        count++;
        setTimeout(countFn, 1000);
        log(count);
    }
    countFn();
    
  • 这个例子是 Timeout 一直未清除,会一直在占用内存 countFn[AAAFFF000]

3. 全局变量没有清除

  • 在项目中,如果定义了全局变量,那么只有等这个页面关闭,项目结束,该变量才会销毁,否则会一直存在

    var gloabV = "data"
    

4. 闭包使用

  • 我们在使用函数的时候,经常会对某些信息进行保护和保存操作,但是在保存的过程中,可能会造成内存无法释放的情况

    function fn() {
        let x = function() {};
        return x;
    }
    let obj = fn();
    
  • 上面代码中,obj 一直在引用 AAAFFF000中的 x。导致这个堆内存无法释放。

  • 记得即使置空 obj=null

5. 事件问题

  • 在JS中,我们有时候会检测某个Element的变化,但是在检测过程中,如果没有及时对检测元素进行解绑操作,可能会造成内存泄漏

    let ele = document.querySelect('ele');
    ele.addEventListener('click', handle);
    let handle = function() { ...do some }
    
  • 以上代码中,没有对事件监听进行解绑操作,handle 会一直被占用,而不会进行垃圾回收。

  • document.body.removeChild('ele') ele.removeEventListener('click') ele = null;

vue的内存泄漏

  • 在vue中,我们的操作,多少不注意也会存在内存泄漏
  • vue官网给我们提供了避免内存泄漏的方法: