# 开始之前
闭包的不恰当使用将会在IE(IE9之前)造成内存泄漏
为什么在IE9之前会产生这样的问题?答案:
IE9的JS引擎用的垃圾回收算法时引用计数法,对于循环引用将会导致GC无法回收应该被回收的内存。造成无意义的内存占用,也叫内存泄漏。
内存泄漏:
程序中动态分配的堆内存由于某种原因没有释放,造成内存上的浪费,导致程序变慢,甚至奔溃
提问:
- 什么是循环引用?、
- GC是什么?怎么工作?
- 为什么引用计数法导致内存无法释放?
- JS还有多少垃圾回收算法?
# 什么是GC
GC就是 Garbage Collection ,,他的作用就是找到内存空间中的垃圾,将其释放,让这么分空间得到再次利用
# 常见的垃圾回收算法
# 引用计数法
就是记录下所有对象有多少人在引用他:
var a = new Object(); // 此时'这个对象'的引用计数为1(a在引用)
var b = a; // ‘这个对象’的引用计数是2(a,b)
a = null; // reference_count = 1
b = null; // reference_count = 0
// 下一步 GC来回收‘这个对象’了
该方法的优点:
- 即刻回收垃圾,引用为0时,立即回收。
- 程序不会暂停去单独使用很长一段时间的GC,最大暂停时间短
- 不用遍历堆里面的所有活动对象和非活动对象
该方法的缺点:
- 计数器占了很大的位置,如果有大量的引用,那么计数器就需要很多位数
- 最大的缺点就是无法解决无法回收的问题
例子:
- 本来f()执行完应该回收函数作用域里面的内存空间
- 但是o里面有属性引用o2,导致o2的引用始终存在,o2里面的o也是如此
- 最终造成了内存泄漏
function f(){
var o = {};
var o2 = {};
o.a = o2; // o 引用 o2,o2的引用次数是1
o2.a = o; // o2 引用 o,o的引用此时是1
return "azerty";
}
f();
所以这种方法被淘汰,在V8引擎里面使用最多的是标记清除算法
# 标记清除算法
主要将垃圾回收过程分为两个阶段:
- 标记阶段:把所有活动对象作标记
- 清除标记:把没有标记的销毁
标记阶段:
- 可达性
GC从全局作用域的变量,沿作用域逐层往里遍历,遍历到堆中对象时,说明对象被引用,打上标记,继续递归遍历(因为对象也会引用另外一个对象),知道遍历到最后一个节点。
清除阶段:
再进行遍历,遍历整个堆,把没有打上标记的对象进行回收
这种算法解决了循环引用的问题,因为在标记的时候循环引用的对象无法从全局对象中获取,则没有被标记,所以在清除阶段时会被回收
优点:
- 实现简单,打标记就两种情况
- 解决循环引用问题
缺点:
- 造成碎片化
- 遍历次数过多