持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情
前言
上一篇文章,说到了闭包的定义以及它在内存中的表现,也知道,如果不及时对闭包的内存进行释放,在不使用的情况下可能会造成内存泄露
,我们这一节就来举个例子看下吧
闭包的内存泄露的例子
来看一段代码
// 函数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左右。
验证
验证的方法也很简单:
- 打开浏览器的开发者模式,点击performance(性能的选项),然后将下方的memory(内存勾选上)
- 点击录制并重新载入页面按钮,也就是下图所示的按钮,浏览器就会自动帮我们,重新执行代码,并录制内存的变化
- 点击录制
- 录制完毕,如图,可见蓝色是js的堆内存,大概是422m,跟我们分析的相吻合,并且它的内存曲线也是随着时间一点点上涨的,跟我们的代码相符、
这就是闭包造成的内存泄漏
试想一下,如果我们后续一直继续生成这个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的垃圾回收机制是定期进行回收的
闭包函数绑定的变量环境中的自由变量在内存中的表现
// 函数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引擎为了优化我们的内存,会把类似的这个变量释放