malloc()
之类的用于内存分配的原始函数,其中某些高级语言(如JavaScript)内置了垃圾收集器来完成此工作。 它跟踪程序的内存分配,并分享程序是否不再使用分配的内存,然后将其自动释放。 但是这种算法无法完全决定是否需要内存。 因此,对于程序员而言,理解并确定特定代码段是否需要内存非常重要。
接下来我们一起来了解垃圾回收如何在JavaScript中工作。
垃圾收集
JavaScript引擎的垃圾收集器基本上会寻找从内存中删除的无法访问的对象。 这篇文章解释以下两种常见的垃圾收集算法:
- 引用计数法
- 标记清除法
引用计数法
这是一个简单的垃圾收集算法。 该算法查找那些没有被引用的对象,如果对象没有附加引用,则可以进行垃圾回收。
var obj1 = {
property1: {
subproperty1: 20
}
};
创建一个如上例所示的对象,以了解该算法。
obj1
指向了一个对象,其property1
中还引用了另外一个对象。 由于obj1
引用了property1
指向的对象,因此此对象是不会被垃圾回收器进行回收的。
var obj2 = obj1;
obj1 = "some random text"
现在,obj2
也具有了对obj1
所引用的同一对象的引用,但是后来obj1
的值被更新为“some random text”,所以现在只有obj2
对引用着这个对象。
var obj_property1 = obj2.property1;
接下来,obj_property1
引用obj2.property1
,该对象也包含一个对象。 因此,该对象现在具有两个引用,如下所示:
- 作为
obj2
的属性 - 变量
obj_property1
obj2 = "some random text"
obj2
被更新为 “some random text” 之后,其也不再引用这个对象。 因此,似乎这个对象没有了其余的引用,因此可以被垃圾回收了。 但这可能是错误的说法,因为obj_property1
具有obj2.property1
的引用。 因此也不会被垃圾收集。
obj_property1 = null;
现在,当我们从obj_property1
中删除引用时,原来位于obj1
中的对象已完全没有引用。 因此,现在可以对其进行垃圾收集了。
该算法在哪些代码中会失效?
function example() {
var obj1 = {
property1 : {
subproperty1: 20
}
};
var obj2 = obj1.property1;
obj2.property1 = obj1;
return 'some random text'
}
example();
在此,引用计数算法不会在函数调用后从内存中删除obj1
和obj2
,因为这两个对象相互引用。
标记清除法
标记清除法会查找从根(JavaScript的全局对象)无法访问的对象。
-
该算法克服了引用计数算法的局限性。
-
没有引用的对象将是不可访问的,反之亦然。
var obj1 = {
property1: 35
}
如上所示,我们可以看到创建的对象obj1
如何从ROOT
可达。
obj1 = null
现在,当我们将obj1
的值设置为null
时,该对象不再可以从ROOT
到达,因此它将被进行垃圾回收。
预备知识
javascript中的内存管理是自动执行的,而且是不可见的
可达性:以某种方式可以访问或者可以用的值,被保证存储在内存中
根:有一些基本的固有可达值,由于显而易见的原因无法删除
本地函数的局部变量和参数
当前嵌套调用链上的其他函数的变量和参数
全局变量
其它...
可访问性:如果引用或引用链可以从根访问其他值,则认为该值是可访问的
标记-清除法 原理:
基本的垃圾回收算法称为“标记-清除”
垃圾回收器获取根并“标记”它们
访问并标记所有来自根的引用
访问标记的对象并标记其引用。
以此类推,直到有未访问的引用为止
除了标记对象,所有对象都被删除
让我们通过查看以下实例来尝试和理解:
如上所示,这就是对象结构的样子。 我们可以注意到从根目录无法访问的对象,但让我们尝试了解“标记并清除”算法在这种情况下的工作方式。
算法开始标记它从根开始可达的对象。 在上图中,我们可以注意到在对象上标记的绿色圆圈。 以便将对象标识为从根可访问。
那些从根目录无法访问的未标记的对象,它们将被垃圾收集。
注意
「垃圾的定义」
没有被引用的对象或者有对象引用,但对象之间为相互引用,根访问不到
如上图所示,尽管对象被引用了,但整体仍然是孤立的,最后仍然会被视为『垃圾』,并进行回收。
局限性
必须使对象明确不可访问,才会进行垃圾收集。
自2012年以来,JavaScript引擎针对引用计数垃圾收集算法进行了修改。
补充:
JavaScript 引擎在垃圾回收方面的优化:
分代回收:对象分为“新对象”和“旧对象”,新对象出现,工作结束后就会被清理干净,存在时间长的对象,就会很少接受检查
增量回收:将垃圾回收分解为多个部分,分别执行
空闲时间收集:垃圾回收器只在CPU空闲时运行
谢谢阅读。