我有一个关于垃圾收集如何在Javascript中运行的快速问题。
如果我有这段代码:
var c = {name : ‘Bob’};
c = {name : ‘Mary’};
在上面的代码中,变量c指向对象{name:'Bob'}。但后来我设置c指向内存中的另一个对象{name:'Mary'}。 c最初指向的对象({name:'Bob'})会发生什么?原始对象是否会在内存中释放,因为不再引用它了?
在另一个案例中:
var c = {name : ‘Bob’};
d = c;
c = {name : ‘Mary’};
现在,c指向的原始对象({name:'Bob'})将不会被释放,因为即使在" c"之后,d仍指向{name:'Bob'}。被更改为指向新对象:{name:'Mary'}。正确的吗?
所以基本上只要存在仍然指向它的引用,对象就不会从内存中释放出来。
如果我正确地考虑这个问题,有人可以向我解释一下吗?
答案 0 :(得分:2)
当没有对它们的引用时,最终正确的对象是垃圾收集,假设你的代码片段在全局范围内,这是真的,当然如果你的var声明在一个函数内,一旦函数有了它就会超出范围退出。一个例外是闭包,其中本地作用域上的对象仍由函数返回的变量引用。
我提到上面收集的'最终'垃圾,因为当实际收集的对象受到各种因素(内存压力等)的影响时,这是特定于正在使用的JavaScript引擎(V8,脉轮,硝基等)。
答案 1 :(得分:2)
你有正确的想法,但有一些细微之处需要注意:
首先,JavaScript运行时决定何时实际运行垃圾收集例程。未使用的对象标记为垃圾收集,而不是立即收集。 GC可能非常昂贵,因此它不会持续运行。
其次,当一个对象变为unreachable时,它就有资格获得GC,而不是简单地没有引用。
如果GC仅考虑引用计数,则可以创建一个"闭环"无法到达,无法收集的物品。
请考虑以下代码段:
var owner = { name: 'Ann' }; // Let's call this object 'Ann'
var pet = { name: 'Zizi' }; // And this one 'Zizi'
// create a loop of references
owner.pet = pet;
pet.owner = owner;
owner = null; // owner no longer refers to Ann
pet = null; // pet no longer refers to Zizi
当此代码完成运行时,没有对Zizi
或Ann
的顶级引用 - 它们无法访问。在现代运行时(如浏览器中的运行时),它们被标记为GC,并且在下一次运行GC例程时将被清除。
但是,如果只在参考计数达到零时收集了对象呢?让我们考虑Zizi
。它无法收集,因为Ann
仍然有引用它。它无法使用,因为没有可用的引用。
Ann
也无法收集,因为Zizi
会引用它。这是一个糟糕的情况 - 用户代码无法访问两个对象,但也无法进行垃圾回收。这是一次内存泄漏。
这种垃圾收集算法,称为引用计数,在旧版本的Internet Explorer中导致infamous issue:DOM节点和事件处理程序可能会阻止彼此进行垃圾收集。由于这个原因,参考计数垃圾收集器基本上已经过时了。
答案 2 :(得分:1)
MDN有一篇关于memory management的非常好的文章。它通过许多例子具体讨论了内存分配和垃圾收集。