前言大家好,我是林三鑫。上一篇告诉你13张图,助你20分钟打败“V8”。Garbagecollectionmechanism”,但是Guan知道回收机制不好。V8的垃圾回收机制很强,但是我们不能随便制造很多垃圾就让它被回收,我们要尽量减少垃圾的产生开发过程中,今天给大家分享一下如何避免JS垃圾过多和内存泄漏,为什么要避免内存泄漏呢,就是有些应该回收的垃圾没有回收,导致越来越多内存泄漏听上去很远,但其实离我们很近,我们都直接或间接接触过它。越来越卡,这时候就需要考虑是不是内存泄露问题,内存泄露是影响用户体验的主要问题,所以通过正确的代码习惯来避免是非常有必要的。如何监控记忆状态W一直强调记忆记忆,但感觉是很虚幻的东西,至少让我们看到它的本来面目吧?浏览器任务管理器打开方式:在浏览器上方右击打开任务管理器:打开后我们可以看到内存和JavaScript内存(括号内):memory:页面中的原始内存,即DOM节点总占用内存JavaScript内存(括号内):页面中所有可达对象占用的总内存。那么什么是可达对象呢?简单点说:就是从初始根对象(window或者global)的指针开始,向下寻找子节点。如果找到子节点,则说明该子节点的引用对象是可达的。如果找不到,说明子节点对象不可达。例如://Reachable,可以通过window.name访问varname='林三鑫'functionfn(){//Unreachable,不能访问varname='林三鑫'}回到我们的任务管理,此时我们在页面中写一段代码:Click点击前:点击后发现内存瞬间增加:Performance使用Chrome浏览器的隐身模式,避免了很多其他影响我们查看内存的因素:按F12打开debug窗口,选择Performance和我们将以掘金首页为例!点击Record->RefreshNuggets->点击stop,可以看到以下指标随时间波动:JSHeap:JS堆Documents:DocumentNodes:DOM节点Listeners:listenerGPUMemory:GPU内存堆快照,heapsnapshot,Asthename隐含的意思,就是对某个page的堆内存进行拍照并保存。在同一个页面上,在执行某个操作之前,记录的堆快照是一个。执行后,记录的堆快照可能是另一个。还是以掘金首页为例,可以看到当前页面内存为13.3M。我们可以选择Statistics来查看数组、对象、字符串等所占用的内存。上面提到的如何避免,其实内存泄漏的问题离我们很近,我们可能直接或间接造成了。接下来说说如何避免这个问题,它也可能是你开发中的一个坏习惯!减少全局变量我们在开发中可能遇到过这样的代码,其实我们只是想把a当作一个局部变量,却忘了写var,let,const:document.getElementById('btn').onclick=function(){//a还没有对外声明a=newArray(1000000).fill('Sunshine_Lin')}上面的代码等价于vardocument.getElementById('btn').onclick=function(){a=newArray(1000000).fill('Sunshine_Lin')}这有什么害处?我们之前谈到了可访问性,这里可以解释一下。上面的代码如果这样写,我们可以通过window.a访问全局变量a,所以a是可达的,不会被当作垃圾回收,导致占用内存不释放,消耗性能,违背我们的初衷。我们可以通过堆快照来验证。步骤为:记录->点击按钮->记录,两次比较结果。点击后内存增加4M。查看Statistics,发现数组内存大了很多,还没有释放:那么应该如何改进呢?可以添加变量定义符号:document.getElementById('btn').onclick=function(){leta=newArray(1000000).fill('Sunshine_Lin')}看效果,由于是局部变量,它是不可达的,每执行一次函数就会被回收释放,所以不会一直占用内存。点击前后的内存是类似的:未清除定时器请看这段代码。这段代码中,fn1函数执行后,按理说会回收arr数组,但是他不能。为什么?因为定时器中的a是指arr,如果不清定时器,a就不会被回收。如果a不被回收,它会一直引用arr,所以arr肯定不会被回收。functionfn(){letarr=newArray(1000000).fill('Sunshine_Lin')setInterval(()=>{leta=arr},1000)}document.getElementById('btn').onclick=function()复制代码{fn()}执行:记录->手动垃圾收集->点击按钮五次->手动垃圾收集->结束第一次和最后两次手动垃圾收集比较第一次和最后两次垃圾内存的最低点次,如果没有内存泄漏的情况下,前两个的最低点应该是一样的。这里可以看到尾部比第一部分多的部分是未回收的内存量。上面说到为什么arr数组没有被回收?是因为定时器没有清零,导致a一直引用arr,怎么解决呢?只需清除计时器即可。functionfn(){letarr=newArray(1000000).fill('Sunshine_Lin')leti=0lettimer=setInterval(()=>{if(i>5)clearInterval(timer)leta=arri++},1000)}document.getElementById('btn').onclick=function(){fn()}再看Performance,发现前两次的内存量是一样的,说明正常并合理使用闭包。我们来看这段代码:functionfn1(){letarr=newArray(100000).fill('Sunshine_Lin')returnarr}leta=[]document.getElementById('btn').onclick=function(){A。push(fn1())}按理说fn1执行完之后,arr会被回收,但是在这段代码中,并没有被回收,为什么呢?因为fn1执行完后返回了arr,然后把arr压入a数组,而a数组是全局变量,a数组是不会被回收的,所以a数组里面的东西自然不会被回收。因此,arr将不会被回收。当你点击的次数越来越多的时候,无法回收的arr就会越来越多。如果a后面没有用到,那么这些arr就变成无用的垃圾了。我们可以通过堆快照通过PerformanceVerify:Performance:Recording->ManualGarbageCollection->点击按钮五次->ManualGarbageCollection->结束第一次和最后两次手动垃圾收集比较两者之间的垃圾内存最低点第一次和最后两次,如果没有内存泄漏的情况下,第一次和最后两次的最低点应该是一样的。这里可以看到tail比first多的部分是未回收的内存量。堆快照:第一次记录->点击按钮5次->第二次记录会发现点击前后,多了很多内存,多出来的内存就是没有被回收的内存量。分离的DOM什么是分离的DOM?或者用代码说话:clickletbtn=document.getElementById('btn')document.body.removeChild(btn)虽然最后删除了button,但是由于全局变量btn引用了这个DOM对象,所以这个DOM对象还没有被回收,调用这个DOM对象中为了分离DOM,我们可以通过heapsnapshot来验证这个问题,在heapsnapshot中搜索detached(中文意思:独立的,分离的):这个问题很好解决,删除按钮后,将btn设置为null方式:点击让btn=document.getElementById('btn')document.body.removeChild(btn)btn=null这个时候button的DOM才真正从js中擦除Go:如何优化参考资料的淘宝前端?如何高效地编写JavaScript?提高JS性能的技巧有哪些?本文将向您展示如何解决内存泄漏导致的页面冻结问题。结语我是林三鑫,一个狂热的前端菜鸟程序员。如果你有上进心,喜欢前端,想学前端,那我们可以交个朋友一起钓鱼哈哈,摸摸鱼群,请加我,请注意[没想到]