垃圾回收
在很多高级语言中,可以自动的分配内存和释放内存。在javaScript中,变量创建的时候为其分配内存,当不再被使用的时候会“自动地”释放这些内存。这个过程就被称为垃圾回收。但是这个可以“自动地”释放资源的本质是是一个混乱的来源。
内存泄漏
- 是什么:内存泄漏是指程序中已动态分配的堆内存由于某种原因程序未释放或者无法释放,造成系统内存的浪费,导致成寻运行速度减慢甚至系统崩溃等严重后果。
- 原因:内存块未被释放,属于遗漏型缺陷而不是过错型缺陷。
- 后果: 内存泄漏缺陷具有隐蔽性、积累性的特种,比其他内存非法访问错误更难检测。在逐渐积累的过程中,会降低系统整体性能,极端的情况下会造成系统崩溃。
内存生命周期
分配内存->使用内存->释放内存。
内存的回收
标记清除法:
- 概述:当变量进入环境(在函数中声明一个变量)时,变量标记“进入环境”。当变量离开环境时,则将其标记为“离开环境”。
- 基本的算法步骤:
- 从‘根’上生成一个列表(通常是以全局变量为根)。在js中
window
对象可以做为一个根。 - 所有的‘根’都被标记为活跃的,所有的子变量也被递归检查。能够从‘根’上到达的都不会被认为成垃圾。
- 没有被标记为或缺的就认为成垃圾。这些内存就会被释放。
- 从‘根’上生成一个列表(通常是以全局变量为根)。在js中
引用计数
- 概述:一个对象在没有被其他的引用只想的时候就被认为“可回收的”。(不常用)
- 缺陷:引用计数在涉及循环引用的时候。例如:创建了两个对象,并且相互引用,这样创建了一个循环。因此他们实际上是没有用的,但是在引用计数算法时考虑到了2个对象每一个都至少使用了一次,所以无法被回收。
function aa(){
var a1={};
var a2={};
a1.aaa = a2;
a2.aaa = a1;
}
f();
内存泄漏
全局变量
- javaScript中,对未声明的变量的引用在全局对象中创建了一个新的变量。
- 通过
this
来创建意外的全局变量 - 在js文件的开头使用
use strict
。可以通过严格模式的解析js来阻止意外的全局变量。 - 如果有时全局变量被用于暂时存储有大量的数据或或者设计到的信息,那么可以在使用完之后指定为null或者重新分配。
定时器或者回调
- 定时器可能会产生对不再需要的DOM节点或者数据的引用。举个例子:
setInterval(function(){
var aa = document.getElementById('aaa');
if(aa){
aa.innerHTML = '......';
}
},1000)
在上面的例子中,aa对象可能会被移除,那么interval将没有存在的意义。但是处理器interval仍然起作用,aa并把不能被回收,如果interval不能被回收,它的依赖也不可能被回收。
- 在目前的浏览器中,可以在对象变得不可到达的时候回收观察处理器,甚至监听器没有没有明确的移除掉。在对象处理之前,最好也要显式地删除这些观察者。
- 现在的浏览器使用现代的垃圾回收算法,可以立即发现并处理这些循环引用。
闭包
一个内部函数使用了外部(封闭)函数的变量。由于js运行的细节,也可能造成内存泄漏
来自DOM的引用
在需要重复操作DOM节点的时候,存储DOM节点是十分有用的。但是需要移除DOM节点的时候,需要确保移除DOM tree和代码中储存的引用。
总结:
- 尽量避免使用全局变量存储大量的数据,如果知识暂时使用,需要在完成之后手动指定为null或者重新分配
- 如果使用了定时器,需要在无用的时候清楚。如果是在DOM节点中绑定了事件监听器,在移除节点时需要先注销事件监听器。
- 在使用闭包时,处理大量数据的时候应该仔细考量。在使用递归的时候也要非常的小心。
- 在移除DOM节点的时候要保证没代码中没有对节点的引用,这样才能完全的移除节点。在移除父级节点之前要先移除子节点。