目录
垃圾回收机制
所谓的内存泄漏简单来说是不再用到的内存,没有及时释放。为了更好避免内存泄漏,我们需要搞清楚Javascript垃圾回收机制。
垃圾回收有两种方法:标记清除、引用计数。目前用的比较多得是标记清除,引用计数会存在循环引用的问题。
如:
var a = {}
var b = {}
a.n = b
b.f = a
当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而 当变量离开环境时,则将其标记为“离开环境”。
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会清除环境中的变量以及被环境中的变量引用的变量的标记。而在此之后还带有标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。
标记-清除算法:
- 垃圾回收机制维护一系列根节点,例如window、global对象等,这些根节点所占用的内存不会被回收;
- 从根节点出发,递归的检查子节点,根节点和所有子节点都标记为引用状态,内存不会被回收
- 所有没有标记的内存块,视为垃圾内存,自动回收,有系统重新分配。
常见的内存泄漏
- 声明全局变量
// (1) 直接声明全局变量
function foo(){
name = 'xxx'
}
// (2) 通过this变量引入
function foo(name){
this.name = name
}
foo('小明')
解决方案:var let const;如果不得不使用全局变量,引用之后,手动设置为null
- setTnterval中引用的变量和对象:未清除定时器,这些变量和对象会一直存在内存中
解决方案:clearInterval()清除定时器
3. dom事件没有移除:dom节点被移除但是事件依然存在内存中
解决方案:removeEventListener()
4. dom移除:移除没有释放子节点的父节点
var $ = document.getElementById;
var tree = $("#tree")
var leaf = $('#leaf') // leaf 是tree中的一个子节点
$('body').removeChild(tree) //tree不能被回收,因为leaf还在
- 闭包
当对象销毁时,手动置为null
参考资料: