【问题标题】:Garbage collector: deleting a reference to an object that has references to another object垃圾收集器:删除对另一个对象的引用的对象的引用
【发布时间】:2012-10-17 18:43:56
【问题描述】:

据我了解,JS 垃圾收集器会删除不再引用的对象。假设我删除了对一个对象的引用,并且该对象具有一个属性,该属性是对另一个对象的引用。两个对象都会被移除吗?

例如

var objectA = {
    prop1: "property",
    prop2: "property"
}

var objectB = {
    refToA: objectA
}

// Remove reference to objectA
delete objectA;

// Remove reference to objectB
delete objectB

现在两个对象都从内存中完全删除了吗?

【问题讨论】:

  • 您的全局变量 objectA 和您的属性 refToA 都引用同一个对象。一旦两个引用都消失了,两者引用的对象就会消失。

标签: javascript


【解决方案1】:

简短的回答:是的。未在任何地方引用的对象将完全从内存中删除,无论该对象的属性引用了什么。

在您的 sn-p 中,事情相当简单,但当您开始使用闭包时,内存管理可能会变得很棘手:

var objA = (function()
{
    var objAb = {who:'An object literal in closure scope',globalReference:'None!'};
    return {who:'Return value, is an object literal that references a closure object'.
            closureObj : objAb};
})();
var objB = {who:'some object',objAb:{who:'object literal, referenced by objB.objAb'}};
var objBAb = objB.objAb;//reference to obj literal, referenced by objB.objAb
var objAb = objA.closureObj;//reference to obj literal, referenced by objA.closureObj, which in turn references the closure object literal

delete objB.objAb;
console.log(objBAb);//<-- the object literal that was declared as a property of objB still exists
delete objAb;//<-- this reference is gone, but the closure obj still exists 
console.log(objA.closureObj);//<-- object is there

基本上,对象是无名的实体。用于访问它们的变量是 referencesnever,ever 包含实际的对象本身。它漂浮在 JS 空间中。当您使用delete someVar 时,您所做的只是取消设置该变量的实际值,即内存地址(某种意义上的)。

如果 JS GC 在内存中找不到任何引用包含对象的位置的变量,它将回收该内存。
就这么简单。

当您将此逻辑应用于以下代码时:

var objA = (function()
{
    var closureObj = {iam:'An object literal defined inside a closure scope'};
    var functionsAreObjects = function()
    {//nameless function object, inside closure scope, too
        return closureObj;
    };
    var resetFunction = function()
    {
        this.closureReference = functionsAreObjects();//assign return value to this
    };
    return {iam:'The returned object literal',
            closureReference:closureObj,
            reset:resetFunction,
            getClosureReference:functionsAreObjects};
})();
delete objA.closureReference;//the closure object IS NOT GC'ed

在前面的示例中,最后一个 delete 语句足以 GC 闭包对象字面量。但是,现在objA 有两种方法(引用函数对象的属性)。这些方法仍然引用闭包对象,仍然被objA引用,所以closureObj还不能被GC'ed。所以,这就是事情变得棘手的地方:

delete objA.closureReference;
delete objA.reset;
delete objA.getClosureReference;

我们已经删除了所有可以链接回closureObj的属性,所以它会被GC'ed是有道理的,对吧? ——呃,不完全是。 Chrome 的 V8 确实会释放内存,但我听说过类似的代码会导致 Opera 中的泄漏,而且 IE 可能不会擅长回收内存,这并非不可能。

另外,我们用getClosureReference 有效地创建了一个getter 方法,所以在现实生活中,这很可能会发生:

//do stuff
delete objA.closureReference;
var objB = objA.getClosureReference();//<-- created new reference to closure object
//do some more stuff
delete objA.reset;
delete objA.getClosureReference;

在这种情况下,closureObj 不能被 GC'ed,因为它仍然在某处被 objB 引用。只有当该变量超出范围时,closureObj 才会被释放。这不仅是反对全局变量的一个非常可靠的论据(它们永远不会超出范围,因此永远不会被 GC'ed),它还表明闭包,尽管它们很整洁,但需要一些开发人员的更多开销:让变量超出范围并不一定意味着内存被释放:某些闭包可能会公开对对象的引用,或者引用该对象的函数...

我已经发布了一个关于这个问题的问题a while ago,但它可能解释了一些事情。
只是为了好玩,如果你想要一个嵌套闭包的例子(闭包中的闭包,闭包中的闭包,将引用传递给彼此和其他对象)try finding out when what can be GC'ed in the following code

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-11
    • 1970-01-01
    • 2011-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-21
    • 2012-08-08
    相关资源
    最近更新 更多