Knockout ViewModel计算垃圾收集

时间:2014-03-29 11:09:07

标签: javascript knockout.js garbage-collection

我一直在尝试在应用程序代码中跟踪垃圾收集的任何问题。我已将其剥离为纯粹的淘汰代码,并且根据计算属性的创建方式,似乎存在收集创建对象的问题。

请参阅以下JS小提琴:http://jsfiddle.net/SGXJG/

  1. 打开Chrome Profiler。
  2. 拍摄快照
  3. 点击全部制作
  4. 拍摄另一个堆快照
  5. 比较快照
  6. Test2&在正确收集Test1时,Test3仍保留在内存中。
  7. 请参阅以下代码了解viewmodel:

    function ViewModel() {
        this.test1 = null;
        this.test2 = null;
        this.test3 = null;
    }
    ViewModel.prototype = {
        makeAll: function () {
            this.make1();
            this.make2();
            this.make3();
        },
        make1: function () {
            this.test1 = new Test1();
            this.test1.kill();
            delete this.test1;
        },
        make2: function () {
            this.test2 = new Test2();
            this.test2.kill();
            delete this.test2;
        },
        make3: function () {
            this.test3 = new Test3();
            this.test3.kill();
            delete this.test3;
        },
    };
    ko.applyBindings(new ViewModel());
    

    以下是三个测试类:

    function Test1() {
        var one = this.one = ko.observable();
        var two = this.two = ko.observable();
        this.three = ko.computed(function () {
            return one() && two();
        });
    }
    Test1.prototype = {
        kill: function () {
            this.three.dispose();
        }
    };
    
    function Test2() {
        this.one = ko.observable();
        this.two = ko.observable();
        this.three = ko.computed(function () {
            return this.one() && this.two();
        }, this);
    }
    Test2.prototype = {
        kill: function () {
            this.three.dispose();
        }
    };
    
    function Test3() {
        var self = this;
        self.one = ko.observable();
        self.two = ko.observable();
        self.three = ko.computed(function () {
            return self.one() && self.two();
        });
        self.kill = function () {
            self.three.dispose();
        };
    }
    

    不同之处在于Test1'三'计算不使用this或self来引用'one'和& '两个'可观察的属性。有人能解释一下这里发生了什么吗?我想封闭包含对象引用的方式有些,但我不明白为什么

    希望我没有错过任何东西。如果我有任何回复,请告诉我。非常感谢。

3 个答案:

答案 0 :(得分:3)

与所有现代浏览器一样,Chrome使用标记和清除算法进行垃圾回收。来自MDN

  

该算法假定知道一组称为根的对象(在JavaScript中,根是全局对象)。垃圾收集器会定期从这些根开始,找到从这些根引用的所有对象,然后从这些根引用所有对象,等等。从根开始,垃圾收集器将找到所有可到达的对象并收集所有非对象可到达的物体。

垃圾收集器不会立即运行,这就是为什么即使你已经取消引用它们,你仍然可以看到Chrome快照中的对象(编辑:如上所述here,运行堆快照首先运行垃圾收集器。可能它没有处理所有内容,所以不清除对象;见下文。)

似乎通常触发垃圾收集器的一件事是创建新对象。我们可以使用您的示例验证这一点。完成问题中的步骤后,单击" Make1"并采取另一个堆快照。您应该看到Test2消失了。现在再做一遍,你会发现Test3也已经消失了。

更多说明:

  1. Chrome在垃圾收集过程中无法清理所有内容。 " [Chrome] ...在大多数垃圾收集周期中仅处理对象堆的一部分。" (google.com)因此,我们看到需要几次垃圾收集器才能清除所有内容。

  2. 只要清除对象的所有外部引用,最终将由垃圾收集器清理该对象。在您的示例中,您甚至可以删除dispose个调用,并清除所有对象。

答案 1 :(得分:2)

我认为这是一个经典的循环引用问题。

我们打电话:

var test2 = new Test2();

现在test2.three拥有test2的参考!因为你确实要求knockout将function(){...}绑定到那个“this”对象,即test2对象。

由于test2自然拥有test2.three的引用,你现在在两个对象之间得到了一个循环引用!

您可以看到这与Test3相同。

但对于Test1,我们打电话:

var test1 = new Test1();

test1.three包含两个对象的引用(test1.one和test2.two),test1包含三个引用(test1.one,test1.two和test1.three),没有循环引用。

在Java和Objective-C等其他语言中,该语言支持弱引用来处理这类问题。但到目前为止,弱引用未在Javascript中实现。

+1谢谢你的提问!它给了我的大脑一些旋转,帮助我更多地了解Javascript:)

答案 2 :(得分:0)

我认为问题在于你使用&&在你的代码中,它将返回一个布尔值,正确的“true”。

this.three = ko.computed(function () {
    //                 !
    return this.one() && this.two();
},

所以this.three == true而不是self.one + self.two如果这是意图? 当你处置

this.three.dispose();

你只需摆脱布尔值。

为什么你在“test2()函数”中有一个额外的“this”?