循环引用在Javascript中 / 垃圾收集器

48

有人能详细解释一下Javascript引擎是如何处理循环引用的吗?不同浏览器或甚至node.js之间是否存在很大差异?

我所指的是对象内部明确的后退/前进引用。例如:

var objA = {
    prop: "foo",
    next: null
};

var objB = {
    prop: "foo",
    prev: null
};

objA.next = objB;
objB.prev = objA;

我们做到了。如果我们执行 console.log( objA ),我们可以看到我们创建了一个无限链。

最重要的问题是,这样做有什么坏处吗?如果不明确清除它,是否会导致内存泄漏?

那么,我们必须怎么做呢?

objA.next = null;
objB.prev = null;

或者说在这样的星座中垃圾收集器会照顾我们吗?

1个回答

73

任何一个还算不错的垃圾回收器都能处理循环引用。

只有在使用简单的引用计数时,循环引用才会成为问题。

大多数垃圾回收器都不采用引用计数,这是因为它不能处理循环引用且效率低下。相反,它们只是遵循从“根”(通常是全局和基于栈的变量)开始找到的每个引用,并将它们标记为“可达性”。

然后它们仅回收所有其他内存。

循环引用并不是问题,因为它们意味着同一节点将被到达多次。在第一次之后,该节点已被标记为“可达”,因此GC将知道它已经在那里过了,并跳过该节点。

即使是基于引用计数的更原始的垃圾回收器也通常实现了检测和打破循环的算法。

简而言之,这不是你需要担心的事情。我似乎记得IE6的JavaScript GC实际上无法处理循环引用(我可能错了,因为我读到这篇文章已经有一段时间了,而且我已经很久很久没有接触IE6了),但在任何现代实现中,这是没有问题的。

垃圾回收器的整个作用就是抽象出内存管理。如果您不得不自己处理这些工作,那说明您的GC有问题。

请参见MDN了解更多关于现代垃圾回收和所使用的标记-清除算法的信息。


1
http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml?showone=Closures#Closures 这个有什么问题吗? - Sandro
2
@Sandro 请再次阅读我的回答。:) 理智的GC可以很好地处理循环引用。所有比IE6更新的浏览器都可以被认为是理智的。如果您需要支持IE6,则必须担心它对循环引用的错误处理。显然,谷歌的指南是基于这样的假设编写的,即必须支持这些有缺陷的浏览器,因此他们必须跳过一些额外的障碍。 - jalf
@Sandro 在这个例子中有一些特殊的事情:DOM元素是循环引用的一部分。通常情况下,直到关闭页面之前你都会泄漏内存。但是如果我没记错的话,IE在导航离开时并不总是删除对DOM的引用。(显然这样做会破坏一些页面?) - Carl Walsh
2
Douglas Crockford也提到了这个IE6的缺陷。我认为现在对于大多数Web应用程序来说,这个问题已经不再相关了。 - mknecht

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接