内存三大件
~内存泄漏 对象已经不再使用但没有被回收,内存没有被释放,即内存泄漏,那想要避免让无用数据还存在引用关系,多注意我们下面说的常见的几种内存泄漏的情况。
~内存膨胀 即在短时间内,内存占用极速上升到达一个峰值,想要避免需要使用技术手段减少对内存的占用。
~频繁 GC 就是 GC 执行的特别频繁,一般出现在频繁使用大的临时变量导致新生代空间被装满的速度极快,而每次新生代装满时就会触发 GC,频繁 GC 同样会导致页面卡顿,想要避免的话就不要搞太多的临时变量,因为临时变量不用了就会被回收,这和我们内存泄漏中说避免使用全局变量冲突,其实,只要把握好其中的度。
什么是内存泄漏
其实js引擎虽然针对垃圾回收做了各种优化从而尽可能的确保垃圾得以回收,但并不是说我们就可以完全不用关心这块了,我们代码中依然要主动避免一些不利于引擎做垃圾回收操作,因为不是所有无用对象内存都可以被回收的,那当不再用到的对象内存,没有及时被回收时,我们叫它 内存泄漏(Memory leak)。
1. 闭包
- JavaScript高级程序设计:闭包是指有权访问另一个函数作用域中的变量的函数
返回的内部函数,引用了外部函数的属性或方法,才会造成内存泄漏;外部函数内部没有被引用,不会造成内存泄漏
大多数人认为的闭包就是,函数内部嵌套并 return 一个函数,这也是闭包
function fn1(){
let test = 1
return function(){
console.log('hellow')
}
}
let fn1Child = fn1()
fn1Child()
显然它是一个典型闭包,但是它并没有造成内存泄漏,因为返回的函数中并没有对 fn1 函数内部的引用,也就是说,函数 fn1 内部的 test 变量完全是可以被回收的。
function fn2(){
let test = 1
return function(){
console.log(test)
return test
}
}
let fn2Child = fn2()
fn2Child()
显然它也是闭包,并且因为 return 的函数中存在函数 fn2 中的 test 变量引用,所以 test 并不会被回收,也就造成了内存泄漏。
其实在函数调用后,把外部的引用关系设置为空就好了
隐式全局变量
全局变量和局部变量,局部变量在函数执行结束后这些变量已经不再被需要,所以垃圾回收器会识别并释放它们。但是对于全局变量,垃圾回收器很难判断这些变量什么时候才不被需要,所以全局变量通常不会被回收。
我们在使用全局变量存储数据时,要确保使用后将其置空或者重新分配,当然也很简单,在使用完将其置为 null 即可,特别是在使用全局变量做持续存储大量数据的缓存时,我们一定要记得设置存储上限并及时清理,不然的话数据量越来越大,内存压力也会随之增高。
DOM引用
代码中进行 DOM 时会使用变量缓存 DOM 节点的引用,但移除节点的时候,我们应该同步释放缓存的引用,否则游离的子树无法释放。如:
<div id="root">
<ul id="ul">
<li></li>
<li></li>
<li id="li3"></li>
<li></li>
</ul>
</div>
<script>
let root = document.querySelector('#root')
let ul = document.querySelector('#ul')
let li3 = document.querySelector('#li3')
</script>
定时器(
setTimeout和setInterval)
// 获取数据
let someResource = getData()
setInterval(() => {
const node = document.getElementById('Node')
if(node) {
node.innerHTML = JSON.stringify(someResource))
}
}, 1000)
在 setInterval 没有结束前,回调函数里的变量以及回调函数本身都无法被回收。
什么才叫结束呢?也就是调用了 clearInterval。如果没有被 clear 掉的话,就会造成内存泄漏。不仅如此,如果回调函数没有被回收,那么回调函数内依赖的变量也没法被回收。
监听者模式
监听者模式想必我们都知道,在Vue 、React中非常常见,比如 EventBus等
当我们实现了监听者模式并在组件内挂载相关的事件处理函数,而在组件销毁时不主动将其清除时,其中引用的变量或者函数都被认为是需要的而不会进行回收,如果内部引用的变量存储了大量数据,可能会引起页面占用内存过高,这样也会造成意外的内存泄漏。
避免内存泄漏: 只需在 beforeDestroy 组件销毁生命周期里将其清除即可。
Map、Set对象
当使用 Map 或 Set 存储对象时,同 Object 一致都是强引用,如果不将其主动清除引用,其同样会造成内存不自动进行回收。
可以采用 WeakMap,WeakMap 对象同样用来保存键值对,对于键是弱引用(注:WeakMap 只对于键是弱引用),且必须为一个对象,而值可以是任意的对象或者原始值,由于是对于对象的弱引用,不会干扰 Js 的垃圾回收。
可以采用 WeakSet,WeakSet 对象允许存储对象弱引用的唯一值,WeakSet 对象中的值同样不会重复,且只能保存对象的弱引用,同样由于是对于对象的弱引用,不会干扰 Js 的垃圾回收。
未清理的Console输出
我们之所以在控制台能看到数据输出,是因为浏览器保存了我们输出对象的信息数据引用,也正是因此未清理的 console 如果输出了对象也会造成内存泄漏。