likes
comments
collection
share

重学javaScript (九)| 关于闭包的内存泄露

作者站长头像
站长
· 阅读数 30

前言

上一篇文章,说到了闭包的定义以及它在内存中的表现,也知道,如果不及时对闭包的内存进行释放,在不使用的情况下可能会造成内存泄露,我们这一节就来举个例子看下吧

闭包的内存泄露的例子

来看一段代码

        // 函数arraydegree  生成内存为4k的数组
        function arraydegree (){
            var arr = new Array(1024*1024).fill(1)
            return function () {
                console.log(arr)
            }
        }
        var arrayfns = []
        for(let i = 0; i<100;i++) {
            setTimeout(()=>{
                arrayfns.push(arraydegree())
            },i*100)
        }

分析

上面的代码是一个典型的闭包的内存泄漏问题,arraydegree函数中的arr大概是4m左右,数字1在内存是2个字节,一个arr是1024*1024个,大概就是4m,在10s中逐步生成100个到函数arrayfns中,它在内存中的变化,应该是逐步上涨的,最终达到400左右。

验证

验证的方法也很简单:

  1. 打开浏览器的开发者模式,点击performance(性能的选项),然后将下方的memory(内存勾选上)

重学javaScript (九)| 关于闭包的内存泄露

  1. 点击录制并重新载入页面按钮,也就是下图所示的按钮,浏览器就会自动帮我们,重新执行代码,并录制内存的变化

重学javaScript (九)| 关于闭包的内存泄露

  1. 点击录制

重学javaScript (九)| 关于闭包的内存泄露

  1. 录制完毕,如图,可见蓝色是js的堆内存,大概是422m,跟我们分析的相吻合,并且它的内存曲线也是随着时间一点点上涨的,跟我们的代码相符、

重学javaScript (九)| 关于闭包的内存泄露

这就是闭包造成的内存泄漏

试想一下,如果我们后续一直继续生成这个4m的数组,而不销毁,它的内存是不是就会越来越大。这个时候就造成了内存泄漏

优化闭包的内存泄漏

来看个代码

        // 函数arraydegree  生成内存为4m的数组
        function arraydegree() {
            var arr = new Array(1024 * 1024).fill(1)
            return function () {
                console.log(arr)
            }
        }
        var arrayfns = []
        for (let i = 0; i < 100; i++) {
            setTimeout(() => {
                arrayfns.push(arraydegree())
            }, i * 100)
        }

        setTimeout(() => {
            for (let i = 0; i < 50; i++) {
                setTimeout(() => {
                    arrayfns.pop()
                }, i * 100)

            }
        }, 10000)

为了方便我们的测试,我们逐步对这个arrayfns函数进行内存的释放,假设我我们只释放一半,也设置定时器来一点点的释放,来观察它在内存中的状态 大概猜一下,应该是过一段时间之后,直接内存耗量减少一半,并不是在准确的2s进行逐步下降的,这是因为js的垃圾回收机制是定期进行回收的

重学javaScript (九)| 关于闭包的内存泄露

闭包函数绑定的变量环境中的自由变量在内存中的表现

        // 函数arraydegree  生成内存为4m的数组
        function arraydegree() {
            var name = 1
            var age = 12
            return function bar () {
                console.log(name)
            }
        }
        var fn = arraydegree()

由上一章可知,函数arraydegree的ao对象是不会被销毁的,按理说没有在bar函数中被调用的变量age,也应该不被销毁,这也是ecma的标准,但是我们的v8引擎为了优化我们的内存,会把类似的这个变量释放