JS垃圾回收机制和内存泄漏

一、浏览器内存泄漏

浏览器封装的V8引擎支持对JS进行解析,当程序运行(runtime)时,只要程序提出需要内存,例如声明赋值字符串、对象、数组等变量时;操作系统就必须给分配内存使用,对于持续运行的服务进程,变量不断增加,并且内存没有得到及时的释放,内存占有会越来越大,轻则影响系统性能,重则直接导致系统崩溃。

1、闭包加引用

    let fn = () =>{
    
    
        let a = 0
        return () => {
    
    
            return a++
        }
    }

    let b = fn()
    console.log(b())    // 0
    console.log(b())    // 1

闭包在IE6下会造成内存泄漏,但是现在已经无须考虑了。虽然变量a无法被回收(相当于全局变量),但是这点内存无须特别考虑,值得注意的是闭包本身不会造成内存泄漏,但闭包过多很容易导致内存泄漏

2、相互引用

let aa = {
    
    }
let bb = {
    
    }
aa.c = bb
bb.c = aa

解决办法:不要互相引用变量

3、全局未声明的变量

    function fn1() {
    
    
        aa = 1  //  window.aa
        console.log(aa)
    }
    fn2()

    function fn2() {
    
    
        this.bb = 1 //  window.bb
        console.log(bb)
    }
    fn2()

函数fn1和fn2均未声明,即将变量绑定到了全局对象window下,这样函数执行完毕均不能释放变量内存。
解决办法:使用严格模式,在js顶部加上‘use strict’

4、定时器和回调函数

    let a = 2
    setInterval(() =>{
    
    
        console.log(a++)
    }, 1000)

当不需要setInterval或者setTimeout时,定时器没有被clear,定时器的回调函数以及内部依赖的变量都不能被回收,造成内存泄漏。

5、DOM对象添加属性

document.getElementById('id')[property] = obj

DOM如果存在会一直绑定这个事件,解决办法如下:

window.onunload = function(){
    
    
  document.getElementById('id')[property] = null
}

7、vue中的定时器,事件监听,$on等
解决办法:在beforeDestroy钩子中移除或关闭相关事件

二、垃圾回收机制

变量的生命周期:当一个变量的生命周期结束之后它所指向的内存就应该被释放。JS有两种变量,全局变量和在函数中产生的局部变量(不包括被引用的变量)。局部变量的生命周期在函数执行过后就结束了,此时便可将它引用的内存释放(即垃圾回收),但全局变量生命周期会持续到浏览器关闭页面。垃圾收集器会检测不再继续使用的变量,然后释放其占用的内存,并按照固定的时间间隔周期性的执行

现代浏览器垃圾回收机制主要有两种:标记清除和引用计数。
1、标记清除(mark-and-sweep)
目前主流IE,Firefox,Opera,Chrome和Safari等浏览器均使用标记清除的垃圾收集策略。
工作原理:是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。

工作流程:
1.垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记。
2.去掉全局中的变量以及被环境中的变量引用的变量的标记。
3.被加上标记的会被视为准备删除的变量。
4.垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。

2、引用计数(reference counting)
引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1,如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值的引用的变量又取得了另外一个值,则这个值的引用次数减1,当这个值的引用次数为0时,则说明没有办法再访问这个值了,因此就可以将其占用的内存空间回收回来。

let aa = []   // 数组[]引用1次
let bb = aa  //  数组引用2次
bb = null  //  释放内存,引用还剩下1次,即变量aa的引用还存在

这样简单的垃圾回收机制,非常容易出现循环引用问题导致内存不能被回收, 进行导致内存泄露等问题; 如:
let aa = {}
let bb = {}
aa.c = bb
bb.c = aa

var wraper = document.querySelector('#btn');
wraper.onclick = handle;
wraper = null;

wraper = null;
该方法并不能解除dom点击事件,因为释放的是wraper引用的这个变量,实际对象DOM已经绑定了这个btn点击事件,举个例子

  let a = {
    
    }
    let b = a
    b.btn = () =>{
    
    }
    b = null
    console.log(a)

猜你喜欢

转载自blog.csdn.net/zxlong020/article/details/108566610
今日推荐