# 开始之前

闭包的不恰当使用将会在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从全局作用域的变量,沿作用域逐层往里遍历,遍历到堆中对象时,说明对象被引用,打上标记,继续递归遍历(因为对象也会引用另外一个对象),知道遍历到最后一个节点。

清除阶段

再进行遍历,遍历整个堆,把没有打上标记的对象进行回收

这种算法解决了循环引用的问题,因为在标记的时候循环引用的对象无法从全局对象中获取,则没有被标记,所以在清除阶段时会被回收

优点:

  • 实现简单,打标记就两种情况
  • 解决循环引用问题

缺点:

  • 造成碎片化
  • 遍历次数过多
最后更新于: 12/25/2020, 9:20:27 AM