内存的释放流程:
- 分配内存
- 内存中的读写
- 垃圾回收
对于内存的使用,所有语言基本都是一样的,只是更底层的语言在对于 ”分配内存“ 和 ”使用内存“ 是明确的,但是在高级语言中(比如本文的 JS)是隐藏了。
JS 中在定义一个变量时,就已经分配好了一个内存;
同时,内部也提供好了垃圾回收的机制,回收那些已经不再使用的内存。
原理:
- 当我们定义了一个变量后,这个变量就会被垃圾回收机制所标记;
- 当一次程序执行以后,如果这个变量依然存在引用,则垃圾回收机制会认为:变量还在使用,不能释放
- 内存泄漏的原因:程序任务一个变量已经没用了,可是垃圾回收机制认为他还在使用,从而导致这段内存无法释放。
- 这里需要注意的是:是程序对这段内存失去了控制权。
- 内存突然暴增,并不是内存泄漏。
上图中,我录制了一个最基础的内存使用:在百度中(input)输入文字,触发高频 change
事件:
于是我们得到了上图中的这条监听内存的曲线:
- 这条蓝色的线条中,我用黄色色圈出来的部分是手动清理了内存后,内存骤降的两个时机
- 黄色圈出来的部分是指程序中,在我操作前后内存的变化,可以看到这里录制前后(停止输入后)内存的使用并没有多出来内存,说明这里的 GC 后内存的控制在程序可掌控的范围内。
以上就是一个简单的内存完整释放的例子。
那么有哪些场景容易造成内存泄露呢?
一、全局变量
分析上图,我们在给 window 变量设置了十万个属性,在这个全局变量下的内存是不会被垃圾回收的,这里多出来的那一步分内存就是程序失去对这些变量控制权的部分:
所以意外的全局变量泄露
二、console.log()
上图执行一段打印逻辑;
将上述结果放大后,可以清晰的看到,两次清理 GC 后,内存得到了一定的释放,但是有超出的那一部分内存造成了内存泄漏:
console.log 也是全局变量
这也是为什么,生产环境中都会要求删除 console 的原因了
当然,让客户看到 console 丢人也是一个原因
三、闭包
闭包本身并不会造成内存泄漏,没有控制好闭包的使用,导致变量的引用无法被回收才是闭包导致内存泄漏的原因。
直接看两次清除 GC 后的情况:
可以看到第二次清除 GC 后,依然有一部分内存没有得到释放,这部分就是闭包引用导致的内存泄漏。
而常规的闭包引用的函数执行并不会造成内存泄漏。
四、Dom 泄漏
现代的前端框架几乎都不推荐直接操作 dom 了,因为 dom 也是对象,创建的 dom 对象在被使用后,没有及时清理掉也会造成一定程度的内存泄漏。
总结
所谓内存泄漏就是程序执行完了,本该随着程序执行结束而被释放的内存由于有着引用关系而未得到释放导致的内存占用问题。
小结一下防止内存泄漏的几个方面:
- 清除不必要的引用
- 尽量不要定义全局变量(比如减少 var 操作符的使用)
- 提交代码前删除 console