Js垃圾收集机制原理
1. 原理
垃圾收集器按照预定的时间间隔,重复执行回收未使用变量所占用的内存。
2. 局部变量的生命周期
在函数执行的过程中,才存在有局部变量,在这个过程中,浏览器引擎会为局部变量在栈或是堆内存上分配相应的空间,以便存储这些值。在函数执行结束后,局部变量就没有存在的必要了,因此可以释放它们所占用的内存以供将来使用。这种情况下会比较好判断变量有没有必要存在了,但是作用域还没有结束,当前变量还被其他地方调用了,那么垃圾收集器就得跟踪这些变量哪个有用哪个没用,做上相应的标记。
3. 垃圾清理实现策略一:标记清除(mark-and-sweep)
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(进入环境标记),变量在环境中被引用时,收集器会去掉进入环境是所加的标记,待变量释放离开环境时,收集器再给其加上“离开环境”的标记,此时,该变量被视为待删除的变量,若一段时间内没有再次被引用,那么收集器就会销毁该变量,并回收其所占的内存。
至于如何加标记,方法有很多,比如通过反转某个特殊位来记录一个变量何时进入环境,或者使用一个“进入环境的”变量列表及一个“离开环境的”变量列表来跟踪哪个变量发生了变化。
4. 垃圾清理实现策略二:引用计数(reference counting)
跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给变量时,则该值的引用次数就是 1 。若同一个值又被赋给另一个变量,则该值的引用次数加 1 ,相反,若包含对这个值的引用次数的变量又取得了另外一个值,则该值的引用次数减 1 。直至该值引用次数为 0 时,说明其再无法访问,此时收集器销毁该变量并回收其所占内存。
计数清除存在的一个问题是,如果有循环引用,即 A 有个指针指向 B,B 也有一个指针指向 A,在采用标记清除策略的实现中,这将导致内存不会被释放。如果 DOM 元素和原生 JS 对象之间创建了循环引用,那就带来内存泄露的问题,解决方法是把 DOM 和 BOM 对象转换成真正的 JS 对象。
5. 垃圾收集的时间间隔:
原来 IE 的垃圾收集器的启动方式是,当内存分配量达到 256 个变量、4096 个对象字面量和数组元素或是 64kb 的字符串这三者任何一个临界值时,就开始启动垃圾收集器。但一个脚本中可能会在其生命周期中一直保有那么多变量,那么垃圾收集器就得一直的去运行,那导致的性能问题是很严重的。所以后面的优化方法是:触发垃圾收集的变量分配的临界值被调整为动态修正。
“JS 是一门具有自动垃圾收集机制的编程语言,开发人员不必担心内存分配和回收的问题,对其垃圾收集例程总结如下:
离开作用域的值将被自动标记为可以回收,因此在垃圾收集期间被删除;
标记清除 是目前主流的垃圾收集算法,其思想是给当前不使用的值加上标记,然后再回收其内存;
另一种收集算法是“引用计数”,其思想是跟踪记录所有值被引用的次数,JS 引擎目前都不再使用该算法,但在 IE 中访问非原生 JS 对象(如 DOM 元素)时,这种算法仍然可能会导致问题,当代码中存在循环引用现象时亦会导致问题;
解除变量的引用不仅有助于消除循环引用现象,而且对垃圾收集也有好处,为了确保有效地回收内存,应该及时解除不再使用的全局对象、全局对象属性以及循环变量的引用。