引用
垃圾回收算法主要依赖于引用的概念。在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。例如:一个JavaScript对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用)。
引用计数垃圾收集
这是最初级的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。
示例
let o = {
a: {
b: 2
}
};
// 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量o
// 显然,两个都不可以被垃圾收集let o2 = o; // o2 变量是第二个对这个对象的引用
o = 1; // 现在,这个对象的原始引用o被o2替换了let oa = o2.a; // 引用这个对象的a属性
// 现在,这个对象有两个引用了,一个是o2,一个是oao2 = "yo"; // 最初的对象现在已经是零引用了,他可以被垃圾回收了
// 它的属性a的对象还在被oa引用,所以还不能回收oa = null; // a属性的那个对象现在也是零引用了
// 它可以被垃圾回收了
限制:循环引用
该算法有一个限制:无法处理循环引用。例:两个对象被创建,并互相引用,形成一个循环。它们被调用之后会离开函数作用域,所以它们以及没有用了,可以被回收了。然而,引用计数算法考虑到它们互相都有至少一次引用,所以它们不会被回收。
function f() {
let o = {};
let o2 = {};
o.a = o2;// o 引用 o2
o2.a = o;// o2 引用 o
return "Dobby";
}
f();
IE6,7使用 引用计数方式 对DOM对象进行垃圾回收。
标记-清除算法
这个算法把“对象是否不再需要”简化为“对象是否可获得”。
这个算法假定设置一个叫做根(root)的对象(在JavaScript里,根是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象。从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。
这个算法比前一个算法好,因为“有零引用的对象”总是不可获得的,相反却不一定,比如“循环引用”。
从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进,并没有改进标记-清除算法本身和它对“对象是否不再需要”的简化定义。
限制:那些无法从根对象查询到的对象都将被清除
尽管这是一个限制,但实践中我们很少会碰到类似的情况,所以开发者不太会去关心垃圾回收机制。