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

Bar*_*lom 15 javascript garbage-collection google-chrome v8 circular-reference

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

Chrome循环参考

它显示了堆中对象的保留器,据我所知并且可以看到,它应该是垃圾,但是尽管如此,它仍然没有被收集.

毕竟,到根的"最短"路径是一条循环路径(它实际上从未到达根路径).令人不禁的是,快照查看器如何能够为它分配12的距离?这只是在放弃之前整个周期所采取的步骤数量吗?注意距离永远不会低于11.

我已经读过,可能需要几次迭代来清理带有循环引用的子图.但是重复强制收集(使用"时间轴"选项卡中的垃圾箱按钮)无法清除这些对象.

请注意,通过'185'引用进行探索最终会导致相同system / Context @862399,因此确实没有从根到此对象的路径(至少在此处不可见).

我疯了,还是垃圾收集器真的坏了?我不记得过去有过这个问题.我在Chrome 45.0.2454.101上.Beta 46.0.2490.64表现相同.

Jes*_*sse 2

说实话,我们需要快速查看一些您复制的测试代码,但我对您所经历的情况有一个大致的了解。如果我错了并且您可以提供一些测试代码来证明这一点,请告诉我。

正如您似乎已经知道的那样,为了“清理”,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 );
Run Code Online (Sandbox Code Playgroud)

您的代码可能比这更复杂,但这可能有助于解释如何在某个地方无法收集垃圾,因为作用域链可以跟踪引用。

考虑以下

function oops(text){

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

// 'doStuff' stays alive thanks to 'test' below.
var test = oops('closure');
Run Code Online (Sandbox Code Playgroud)

该函数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;
Run Code Online (Sandbox Code Playgroud)

这本质上是我们创建的内存泄漏。每次调用时,oops您都会创建一个具有唯一标识符的函数对象doStuff

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

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

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

var test = oops( 'closure' );
Run Code Online (Sandbox Code Playgroud)

现在我们没有内存泄漏了。doStuff正在被调用而不是被创建

仔细查看您的代码,您会发现您可能在某处执行此操作。

如果您正在搞乱元素(我认为您可能会搞乱),IBM 有一篇关于循环引用的好文章,您可能需要看一下。


现在已经很晚了,其中一些内容尚未经过测试,但理论仍然存在,因此如果我拼写错误等,请告诉我,我明天可以为该页面的未来访问者查看。