JavaScript垃圾回收
javascript垃圾回收主要分成两种方法:
1. 引用计数
引用
垃圾回收算法主要依赖于引用的概念。
在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。
例如,一个Javascript对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用)。
在这里,“对象”的概念不仅特指 JavaScript 对象,还包括函数作用域。
引用计数垃圾收集
这是最初级的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。
1 | var o = { |
2. 标记清除
JavaScript 中最常用的垃圾收集方式是标记清除。它的具体工作步骤如下:
- 给存储在内存中的所有变量加上标记(当然可以使用任何标记方式)
- 去掉当前执行环境中的变量,以及被执行环境中的变量引用的变量的标记
- 第二步结束后仍被标记的变量将被视为准备删除的变量,因为此时的执行环境中的变量已经无法访问到这些变量了。
- 完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。
对于如下代码:
1 | var a = 1, |
假设执行到test2()时,垃圾回收开始执行
对于此时,内存情况为
开始第一步,将内存中的所有变量加上标记
去掉当前执行环境中的变量,以及被执行环境中的变量引用的变量的标记。简单分析一下,此时执行环境中可以访问的变量有 d ,然后我们通过作用域链可以访问到全局变量对象,因此 a 和 b 我们也是可以访问到的
此时只有 c 变量还保留有标记,说明 c 变量通过此时的环境已经访问不到了,所以 c 变量需要被清除掉来释放内存。
最后垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。
缺点:
- 标记清除法的第一个问题就是效率不高,因为在标记清除-阶段,整个程序将会等待,所有如果程序出现卡顿的情况你,那么就有可能是收集垃圾的情况。
- 标记清除法的第二个问题是,从上面的例子我们可以看出,在清除之后内存空间不是连续的,即出现了内存碎片。如果后面需要一个比较大的连续的内存空间时,那将不能满足要求。而标记-整理方法可以有效地解决这个问题。与标记清除法相比,标记阶段没有什么不同,只是标记结束后,标记-整理方法会将活着的对象向内存的一边移动,最后清理掉边界的内存。不过可以想象,这种做法的效率没有标记-清除高。
对比一下官方文档的解释: