@Dreamingboy
2017-09-24T08:54:25.000000Z
字数 2074
阅读 752
JavaScript
JavaScript具有自动的垃圾收集机制,也就是说在一定的周期内,浏览器的垃圾回收机制都会进行一次扫描,然后将一些不再有用的变量和对象回收,释放内存。
常见的垃圾收集机制有下面两种:
上面这段话其实可以相对简单的概括为:只有那些存在于环境中以及能够被环境引用的变量才不会被回收
下面这张图是一张很经典的说明这个原理的图:

红色的圆圈代表的是根对象,也就是在JavaScript中常说window对象。需要注意的是root是永远不会被回收的。
假设现在开始执行垃圾回收,那么就会从root开始,然后遍历每个对象、函数、变量。并且依次为它们做上标记。假设现在在全局作用域中具有两个函数2和3,这样的话2和3就可以被root引用到,也就是处于环境中,然后2和3又通过别的一些方法连接到4,5,6,7,8,这样的话其实4,5,6,7,8其实都是处于这个环境之内,而9和10处于这个环境之外,因此最后只有9和10会被回收。
下面通过一个例子来看一下:
function example(name){let object = {a:1,b:"name",name:name,sayName:function(){console.log(this.name);}};return object;}let object1 = example("mike");object1.sayName();let object2 = example("bob");
首先说明一点,这个函数是在全局作用域中i运行的,接下来我们一步步来说明垃圾回收的过程
首先在全局作用域中创建example函数,然后在下面调用了example创建了一个对象,返回这个对象,变量object1指向创建的对象。
假设现在垃圾开始进行垃圾回收:首先由于example这个函数是在全局作用域的,所以其与root之间存在联系,不会被回收,接下来调用了example这个函数创建了一个对象,但是由于object1这个全局变量指向这对象,那么这个对象也不会被回收。
把上面的例子稍微改变了一下,看看下面这种情况:
function example(name){let object = {a:1,b:"name",name:name,sayName:function(){console.log(this.name);}};return object;}let object1 = example("mike");object1.sayName();object1 = null;example = null;let object2 = example("bob");//报错!
在中间加了两句,将object1、example设置为null之后,相当于切断了object1和对象的连接以及example和函数的连接这样的话当再次进行垃圾回收的时候example这个函数和object1指向的那个对象就会被回收。
var a = { name:"bob",age:10 };var b = a;a = null;
上面这个例子中,首先创建了一个对象,然后用a这个变量去引用这个对象,这样话对象的引用次数是1,接着创建了一个新的变量b指向对象,增加了对象的引用次数,现在对象的引用次数是2,接下来将a赋值为null,对象的引用次数减少1。
但是,引用计数存在一个问题就是循环引用,看看下面这个例子:
function temp1(){var temp2 = new Object();var temp3 = new Object();temp2.nextObject = temp3;temp3.nextObject = temp2;}
上面这个例子中,就算这个函数执行结束也还是两个对象的引用次数都是非零,所以这两个对像永远都不会被垃圾回收。所以这就有可能引发内存泄露
一般解决这个问题都是手动将引用设置为null。像上面这个例子其实可以通过下面这种方法来实现
function temp1(){var temp2 = new Object();var temp3 = new Object();temp2.nextObject = temp3;temp3.nextObject = temp2;temp2 = null;temp3 = null;temp2.nextObject =null;temp3.nextObject =null;}
所以,现在主要的垃圾回收机制都是采用标记清除。