理解JavaScript中的标记和扫描算法



根据MDN,标记和扫描是比引用计数更好的垃圾收集算法,因为它可以避免循环引用可能发生的内存泄漏。但是,我不明白为什么会这样?有人能(用代码(解释"标记和扫描"是如何避免此类内存泄漏的吗?请随意使用MDN的以下代码,它解释了引用计数的工作原理:

var x = { 
a: {
b: 2
}
}; 
// 2 objects are created. One is referenced by the other as one of its properties.
// The other is referenced by virtue of being assigned to the 'x' variable.
// Obviously, none can be garbage-collected.
var y = x;      // The 'y' variable is the second thing that has a reference to the object.
x = 1;          // Now, the object that was originally in 'x' has a unique reference
//   embodied by the 'y' variable.
var z = y.a;    // Reference to 'a' property of the object.
//   This object now has 2 references: one as a property, 
//   the other as the 'z' variable.
y = 'mozilla';  // The object that was originally in 'x' has now zero
//   references to it. It can be garbage-collected.
//   However its 'a' property is still referenced by 
//   the 'z' variable, so it cannot be freed.
z = null;       // The 'a' property of the object originally in x 
//   has zero references to it. It can be garbage collected.

此外,全局变量是否从未被垃圾收集?

标记扫描的工作原理是标记根,如临时变量(在当前方法中(和全局(静态(变量,然后扫描堆,删除任何未标记的引用

例如,示例中的第一行创建了一个嵌套对象,并将其分配给变量x。为了让MS知道其字段b是否有效,它首先标记根。

这是通过检查当前方法(框架(中的所有全局变量和局部变量,然后标记它们所指向的引用来实现的。例如,一个是x的外部对象。

这里必须注意的是,内部对象布局中的每个对象都(至少(保留了两个位,用于标记对象是否被引用。

var x = { a: {b: 2}}; 

因此,包含字段a的外部对象被标记为活动对象,因为它被局部变量引用。现在,它还必须标记其字段引用的对象。字段a和a.b就是这样的对象。一旦标记了对象的字段及其字段引用的所有引用,就标记了外部对象及其内部对象的第二位。

为了简单起见,我们假设这是堆上唯一的活动对象。一旦标记阶段完成,就可以扫描堆(可以将其想象为一个数组或通用对象列表(。至此,所有对象要么是完全标记的(2位(,要么是未标记的,也就是没有引用。所有未标记的对象都可以被释放,它们用来共享的内存可以被重新使用。

现在循环引用的问题是,一个对象a引用另一个对象b,反之亦然。引用计数的情况是,只要一个对象对自己有引用,其引用计数字段将至少为1。问题是,没有根对象或根间接引用的对象(也称为活动对象(引用循环对象,但根据算法,引用计数器等于或高于1意味着它是活动的。

标记扫描对活动对象的定义不同,即它被根对象直接或间接引用。

最新更新