Javascript Chrome/V8不垃圾收集循环引用?

Javascript Chrome/V8不垃圾收集循环引用?,javascript,google-chrome,garbage-collection,v8,circular-reference,Javascript,Google Chrome,Garbage Collection,V8,Circular Reference,查看Chrome堆快照的这一部分: 它显示了堆中某个对象的保持器,据我所知和所见,该对象应该是垃圾,但尽管如此,它还是没有被收集 到根的“最短”路径毕竟是一条循环路径(它从未真正到达根)。这让人不禁要问,快照查看器如何能够为其指定12的距离?这仅仅是它在放弃之前所采取的步骤吗?请注意,距离永远不会低于11 我读到过,用循环引用清理子图可能需要几次迭代。但重复强制收集(使用时间线选项卡中的垃圾桶按钮)无法清理这些对象 请注意,通过“185”引用进行探索最终会得到相同的system/Context

查看Chrome堆快照的这一部分:

它显示了堆中某个对象的保持器,据我所知和所见,该对象应该是垃圾,但尽管如此,它还是没有被收集

到根的“最短”路径毕竟是一条循环路径(它从未真正到达根)。这让人不禁要问,快照查看器如何能够为其指定12的距离?这仅仅是它在放弃之前所采取的步骤吗?请注意,距离永远不会低于11

我读到过,用循环引用清理子图可能需要几次迭代。但重复强制收集(使用时间线选项卡中的垃圾桶按钮)无法清理这些对象

请注意,通过“185”引用进行探索最终会得到相同的
system/Context@862399
,因此实际上没有从根到该对象的路径(至少在这里不可见)


我是疯了,还是垃圾收集器真的坏了?我不记得以前有过这个问题。我用的是Chrome 45.0.2454.101。Beta 46.0.2490.64的行为与此相同。

老实说,我们需要快速查看一些测试代码,您在其中复制了这些代码,但我对您的体验有一个大致的了解。如果我错了,你可以提供一些测试代码来证明这一点,请让我知道

正如您似乎已经知道的,为了“清理”,Javascript不希望再引用要释放的项

一个简单的例子:

// Currently not eligible for garbage
var myObj = {
    data : 'test'
};

// Reference data inside myObj
// A unique identifier to myObj.data now exists 
var myData = myObj.data;

// Whoops
myObj = 'something else';

// Because myData exists, so does data and can not be freed either, understandable
// This sounds simple and logical but most do not understand what is happening in the engine
// A relationship has been born between 'myData' and 'data' and as long as it exists, it is not eligible for garbage and remains in memory
console.log( myData );
您的代码可能比这更复杂,但这可能有助于解释为什么在某个地方,垃圾不能被收集,因为范围链可以被跟踪到引用

考虑以下几点

function oops(text){

    function doStuff(){
        return text.toLowerCase();
    }
    return doStuff();
}

// 'doStuff' stays alive thanks to 'test' below.
var test = oops('closure');
函数
doStuff
不会被垃圾收集,因为它正被
test
引用

// You can see where this is headed. We have 2 references to the same 'doStuff' function object with separate unique identifiers now below.
var test2 = oops('closures...');

// This is now another unique identifier to 'doStuff'
var test3 = test2;

// doStuff survives yet still
test = 0;
test2 = 0;

// Now we let the function object of 'doStuff' be freed because there is no longer any references to it
test3 = 0;
这本质上是我们造成的内存泄漏。每次调用
oops
时,您都在创建一个具有唯一标识符的函数对象
doStuff

避免这种情况的一种方法是

function doStuff( text ){
    return text.toLowerCase();
}

function oops( text ){
    return doStuff();
}

var test = oops( 'closure' );
现在我们没有内存泄漏
doStuff
正在被调用而不是创建

仔细看看你的代码,你会发现你可能在某处这样做

如果您正在处理一些元素,我认为您可能正在处理这些元素,那么您可能想看看



现在已经很晚了,其中一些方法还没有经过测试,但理论仍然存在,因此如果我拼写错误,请告诉我,明天我可以查看一下,以便将来访问此页面。

这些方法甚至可以被垃圾收集吗?您正在创建闭包吗?您是否会无意中造成内存泄漏?的示例中有一个闭包,它创建了一个循环引用,从而导致内存泄漏。如果这是OP的情况,那么这并不是什么新鲜事:。不知道为什么,但现代JS引擎似乎没有解决这一非常常见的内存泄漏问题的解决方案。@GOTO0我知道闭包保留了对它使用的封闭变量的引用(否则它怎么能工作?),但它也保留了未使用的封闭变量,这对我来说是新的,实际上相当令人失望。然而,该风格指南称“循环引用,因此,内存泄漏”。这不是只适用于引用计数GC的过时语句吗?我还以为马克和斯威普会让这件事不成问题呢?如果这部分是错误的,那么该指南的其余部分是否可信?@GOTO0我做了一个小测试:。即使
x
返回的闭包被
窗口
保留,在那里创建的
MyClass
实例
m
也不会被保留(如堆快照中所示)。将保留
y
中的一个,因为由于
eval
无法对其进行优化。(这是V8中的一个实现细节,虽然我怀疑其他现代浏览器也有类似的行为,但我还没有对此进行测试。)Google风格的代码链接只是一些人的建议,尽管是来自Google的建议,所以任何建议文章,你都应该这样认为。关于这一点,有一些众所周知的老文章是基于证据的,其中有一些很棒的建议和例子。然而,你会发现最好的文档是你的chrome配置文件和内存使用情况。使用您的应用程序一段时间,然后让它保持打开状态,这将比一篇文章或我的/任何答案更能帮助您识别内存管理的机会。感谢您的回答,但如上所述,我已经意识到闭包的保留能力,尽管显然还不够充分。尽管如此,如果堆快照中的值可以从GC根目录访问,为什么不显示一个完整路径,并将距离减小到1?这是否意味着快照查看器实际上不受信任?正如在问题中所说,我过去没有遇到过这个问题(尽管很难说是应用程序或Chrome中的更改导致了这个问题)。我曾尝试在示例代码中重现这个问题,但没有成功。应该说,它出现的实际应用程序相当大,是一个相当大的单页游戏。它的内存使用率(泄漏前)可以达到几百兆。我不知道浏览器是否已经准备好了?当然,如果内存使用合理,浏览器可以处理几百兆,但是内存越多,延迟就越高。如果我可以推荐的话。拍摄一张快照,使用您的应用程序一段时间,然后拍摄另一张并进行比较。对于dom元素,您可能会像本文中的人一样找到优化的地方::我认为您使用内部函数的示例是错误的,不会泄漏。也许Jesse无意中编写了“return doStuff()”。我认为他的意思是“返回多斯塔夫”(不调用多斯塔夫)