垃圾回收机制
自动垃圾收集机制
- javascript具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。即开发人员不需要当心内存使用的问题,所需内存的分配以及无用内存的回收完全实现了自动管理。这种垃圾回收机制的原理:找出那些不再继续使用的变量,然后释放其内存。为此,垃圾收集器会按照固定的时间间隔,周期性地执行这一操作。
局部变量的生命周期
- 局部变量只在函数执行的过程中存在。在这个过程中,会为局部变量在栈内存或堆内存上分配相应的空间,以便存储他们的值。然后再函数中使用这些变量,直至函数执行结束。此时,局部变量就没有存在的必要了,因此可以释放他们的内存以供将来使用。
javascript中的2种垃圾回收机制
标记清除(Mark-and-sweep)
- 包括标记阶段和清除阶段。
- 标记阶段:垃圾回收器创建了一个
roots列表。Roots通常是代码中全局变量的引用。javascript中,window对象是一个全局变量,被当作root。window对象总是存在,因此垃圾回收器可以检查他和他所有子对象是否存在(即不是垃圾)。所有的roots被检查和标记为激活(即不是垃圾)。所有的子对象也被递归检查,检查从root开始的所有对象,如果是可达的,则打上标记。标记阶段到此结束。 - 清除阶段:标记阶段完成时,被标记的对象被视为“存活”对象。然后重新进入递归扫描阶段,将没有标记的对象进行回收。这一阶段称为清除阶段。在扫描的同时,还需要将存活的对象的标记清除掉,以便下一次GC操作做准备。
引用计数(reference counting)
- 引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则将该引用次数再加1。相反,如果包含对这个值得引用变量又取得了另一个值,则这个值的引用次数就减1。当这个值得引用次数变为0时,则说明没有办法在访问这个值了,因而久可以将其占用的内存空间回收回来。这样,垃圾回收器下次再运行时,会释放那些引用次数为0的值所占用的内存。
内存泄漏
- 内存泄漏是指应用程序使用过的内存片段,在不需要的时候,不能返回到操作系统或可用的内存池中的情况。javascript中的内存泄漏同理,当一个值不再需要被引用了,但却无法被垃圾回收器回收的情况,就是内存泄露。
javascript中3种常见的内存泄漏
1. 意外的全局变量
举个例子(普通变量):
1 | function fun(){ |
再举个例子(由this创建的变量):
1 | function foo() { |

- 解决方案:在js文件头部加上(ES5)
use strict,使用严格模式,可以避免此类错误的发生。如果必须使用全局变量存储大量数据时,确保用完以后把它设置为 null 或者重新定义。
2.被遗忘的计数器或者回调函数
举个例子:
1 | var someResource = getData(); |
- 对于观察者的例子,一旦它们不再需要(或者关联的对象变成不可达),明确地移除它们非常重要。老的 IE 6 是无法处理循环引用的。如今,即使没有明确移除它们,一旦观察者对象变成不可达,大部分浏览器是可以回收观察者处理函数的。
3. 脱离DOM的引用
- 有时,保存 DOM 节点内部数据结构很有用。假如你想快速更新表格的几行内容,把每一行 DOM 存成字典(JSON 键值对)或者数组很有意义。此时,同样的 DOM 元素存在两个引用:一个在 DOM 树中,另一个在字典中。将来你决定删除这些行时,需要把两个引用都清除。
1 | var elements = { |
4.闭包(正常情况下不会造成内存泄漏)
闭包就是能够读取其他函数内部变量的函数,闭包的出现时为了突破作用域链的限制。
由于IE9 之前的版本对JScript 对象和COM 对象使用不同的垃圾收集。因此闭包在IE 的这些版本中会导致一些特殊的问题。具体来说,如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁。
reference – javascript高级程序设计第三版
举个例子:
1 | function closure(){ |
- 以上代码创建了一个作为element 元素事件处理程序的闭包,而这个闭包则又创建了一个循环引用。由于匿名函数保存了一个对closure()的活动对象的引用,因此就会导致无法减少element 的引用数。只要匿名函数存在,element 的引用数至少也是1,因此它所占用的内存就永远不会被回收。
解决方案:把element.id 的一个副本保存在一个变量中,从而消除闭包中该变量的循环引用同时将element变量设为null。
1 | function closure(){ |
因此可以看出,闭包并不会引起内存泄漏,只是由于IE9之前的版本对JScript对象和COM对象使用不同的垃圾收集,从而导致内存无法进行回收,这是IE的问题,所以正常使用闭包的情况下不会导致内存泄漏。