JavaScript内存泄漏的处理方式(4)

在周期方面有一个限制。例如下面的例子,创建两个对象并相互引用,这样会创建一个循环引用。在函数调用之后,它们将超出范围,所以它们实际上是无用的,可以被释放。然而,引用计数算法认为,由于两个对象中的每一个都被引用至少一次,所以两者都不能被垃圾收集机制收回。

function f() {
var o1 = {};
var o2 = {};
o1.p = o2; // o1 references o2
o2.p = o1; // o2 references o1. This creates a cycle.
}
f( );

 

标记和扫描算法

为了决定是否需要对象,标记和扫描算法会确定对象是否是活动的。

标记和扫描算法经过以下3个步骤:

roots:通常,root是代码中引用的全局变量。例如,在JavaScript中,可以充当root的全局变量是“窗口”对象。Node.js中的相同对象称为“全局”。所有root的完整列表由垃圾收集器构建。然后算法会检查所有root和他们的子对象并且标记它们是活动的(即它们不是垃圾)。任何root不能达到的,将被标记为垃圾。最后,垃圾回收器释放所有未标记为活动的内存块,并将该内存返回给操作系统。

这个算法比引用计数垃圾收集算法更好。JavaScript垃圾收集(代码/增量/并发/并行垃圾收集)领域中所做的所有改进都是对这种标记和扫描算法的实现改进,但不是对垃圾收集算法本身的改进。

周期不再是问题了

在上面的相互引用例子中,在函数调用返回之后,两个对象不再被全局对象可访问的对象引用。因此,它们将被垃圾收集器发现,从而进行收回。

即使在对象之间有引用,它们也不能从root目录中访问,从而会被认为是垃圾而收集。

抵制垃圾收集器的直观行为

尽管垃圾收集器使用起来很方便,但它们也有自己的一套标准,其中之一是非决定论。换句话说,垃圾收集是不可预测的。你不能真正知道什么时候进行收集,这意味着在某些情况下,程序会使用更多的内存,虽然这是实际需要的。在其它情况下,在特别敏感的应用程序中,短暂暂停是很可能出现的。尽管非确定性意味着不能确定何时进行集合,但大多数垃圾收集实现了共享在分配期间进行收集的通用模式。如果没有执行分配,大多数垃圾收集会保持空闲状态。如以下情况:

大量的分配被执行。大多数这些元素(或所有这些元素)被标记为无法访问(假设我们将一个引用指向不再需要的缓存)。没有进一步的分配执行。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/786.html