2015-10-17 153 views
2

我有一个关于垃圾收集如何在Javascript中工作的快速问题。Javascript内存释放

如果我有这样的代码:

var c = {name : ‘Bob’}; 
c = {name : ‘Mary’}; 

在变量c上面的代码所指向的对象{名称:“鲍勃”}。但是,我设置c指向内存中的另一个对象{name:'Mary'}。该c最初指向的对象({name:'Bob'})会发生什么?由于没有引用它,该原始对象是否会在内存中被释放?

在另一种情况:

var c = {name : ‘Bob’}; 
d = c; 
c = {name : ‘Mary’}; 

现在,原来的对象是c是指向({名称:“鲍勃”}):“鲍勃”不会因为d仍然指向{名称被释放}即使在“c”被改为指向新对象之后:{name:'Mary'}。正确?

所以基本上,只要仍有指向它的引用,对象将不会从内存中释放。

有人请向我解释,如果我正确地思考这个问题吗?

+2

是的,原则上这是正确的。但是,*发生垃圾收集时完全取决于浏览器,因此经常使用表达式“可用于垃圾回收”而不是“垃圾回收”。 – RobG

回答

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 

当这个代码运行结束,没有顶层引用ZiziAnn - 他们是可达。在现代运行时(如浏览器中的运行时)中,它们被标记为GC,并在下一次运行GC例程时被清除。

但是,如果只有当对象的引用次数达到零时才收集对象,该怎么办?我们来考虑一下Zizi。它不能收集,因为Ann仍然有一个参考。它不能使用,因为没有可用的引用。

Ann也不能收集,因为Zizi引用它。这是一个糟糕的情况 - 用户代码无法达到的两个对象,但也无法收集垃圾。这是内存泄漏。

这种垃圾收集算法,被称为引用计数的,在旧版本的Internet Explorer引起了infamous issue:DOM节点和事件处理程序可以阻止对方从不断被垃圾收集。因为这个原因,参考计数垃圾收集器基本上已经过时。

延伸阅读:Chrome Devtools Docs - Memory 101

+0

嗯,好的!我发现链接有点混乱哈哈...也许是因为我不熟悉内存管理,但我有一个关于你的代码的问题。当您设置owner = null和pet = null时,该行会做什么?它是否会释放所有者和宠物对象?你是不是也只是在你的例子中将鲍勃称为所有者?我不确定鲍勃来自哪里哈哈 – LP496

+1

对不起,鲍勃从早先的编辑中遗留下来!我已经更新并希望现在澄清。赋值为null不会立即释放对象,但它确实会使它们无法访问,因为'owner'和'pet'不再引用它们。你不能在这个程序结尾编写代码,以某种方式获得对'Ann'或'Zizi'的引用。因为它们无法访问,所以它们被标记为GC(在现代运行时中)。 – joews

+0

哦,我终于明白了无法达到的概念!另一位用户@Leo Nix发布了一篇文章,指出JS使用标记和扫描算法,而不是引用计数来修复引用的内存泄漏问题。感谢您澄清! – LP496

2

正确的对象最终是垃圾收集,当没有对它们的引用时,假设你的代码片段在全局范围内,这是真的,当然如果你的var声明在一个函数内,它将超出函数的范围已经退出。此例外是闭包,其中本地作用域上的对象仍由该函数返回的变量引用。

我上面提到'最终'的垃圾收集,因为实际收集对象时会受到各种因素(内存压力等)的影响,这是特定于所使用的JavaScript引擎(V8,脉轮,硝基等)。

1

MDN有关于memory management的一篇非常好的文章。它具体讨论了内存分配和垃圾回收的例子。

+0

哦,这绝对清理了很多。我只是有一个快速的问题:在限制:循环在该函数中的头每个对象引用对方。如果我们在该函数中调用return之后使用标记和扫描算法,那么“2个对象(o和o2)不再被全局对象可访问的东西引用”?是否因为该函数的执行堆栈被释放了? – LP496

+0

这两个变量/对象的作用范围是本地的,所以一旦函数执行完毕,它们就不能再被任何东西访问,变得毫无用处。因此,标记和扫描可以释放它们,而引用计数表明每个引用都被引用过一次,因此不符合垃圾回收的条件。 –

+0

啊,好的!我刚刚意识到这一点。你非常喜欢这个链接!帮了一吨! – LP496