Javascript 敲除ViewModel计算的垃圾回收

Javascript 敲除ViewModel计算的垃圾回收,javascript,knockout.js,garbage-collection,Javascript,Knockout.js,Garbage Collection,我一直在努力跟踪我的应用程序代码中的垃圾收集问题。我已经将其剥离为纯敲除代码,根据计算属性的创建方式收集创建的对象似乎存在问题 请参阅以下JS fiddle: 打开Chrome分析器 拍摄堆快照 单击全部生成 拍摄另一个堆快照 比较快照 正确收集Test1时,Test2和Test3仍保留在内存中 请参阅viewmodel的以下代码: function ViewModel() { this.test1 = null; this.test2 = null; this.test

我一直在努力跟踪我的应用程序代码中的垃圾收集问题。我已经将其剥离为纯敲除代码,根据计算属性的创建方式收集创建的对象似乎存在问题

请参阅以下JS fiddle:

  • 打开Chrome分析器
  • 拍摄堆快照
  • 单击全部生成
  • 拍摄另一个堆快照
  • 比较快照
  • 正确收集Test1时,Test2和Test3仍保留在内存中
  • 请参阅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来引用“一”和“二”可观察属性。有人能解释一下这里发生了什么吗?我想在闭包包含对象引用的方式中有些东西,但我不明白为什么


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

    我认为问题在于您在代码中使用&&时,它将返回一个布尔值,正确地说是“true”

    那么这个.three==真的而不是self.one+self.two,如果这是意图? 当你处理

    this.three.dispose();
    
    你只要去掉一个布尔值


    “函数Test2()”中有一个额外的“this”是有原因的吗?

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

    让我们打电话:

    var test2 = new Test2();
    
    var test1 = new Test1();
    
    现在test2.3包含了test2的引用!因为您确实要求knockout将函数(){…}与“this”对象test2对象绑定

    由于test2自然地持有test2.3的引用,因此现在在两个对象之间获得了一个循环引用

    您可以看到这与Test3相同

    但是对于Test1,我们调用:

    var test2 = new Test2();
    
    var test1 = new Test1();
    
    test1.three包含两个对象的引用(test1.one和test2.two),test1包含三个引用(test1.one、test1.two和test1.three),没有循环引用

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


    +谢谢你的提问!它让我的大脑有些旋转,帮助我更了解Javascript:)

    与所有现代浏览器一样,Chrome使用标记和清除算法进行垃圾收集。发件人:

    该算法假设一组称为根的对象(在JavaScript中,根是全局对象)的知识。垃圾收集器将定期从这些根开始,查找从这些根引用的所有对象,然后查找从这些根引用的所有对象,等等。因此,从根开始,垃圾收集器将查找所有可访问的对象并收集所有不可访问的对象

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

    通常触发垃圾收集器的一件事是创建新对象。我们可以用你的例子来验证这一点。完成问题中的步骤后,单击“Make1”并拍摄另一个堆快照。您应该看到Test2已经消失了。现在再做一次,您将看到Test3也消失了

    还有一些注意事项:

  • 在垃圾收集过程中,Chrome不会清理所有东西。“[Chrome]…在大多数垃圾收集周期中只处理对象堆的一部分。”()因此,我们看到需要运行几次垃圾收集器才能清除所有内容

  • 只要清除了对对象的所有外部引用,垃圾收集器最终就会清除该对象。在您的示例中,您甚至可以删除
    dispose
    调用,所有对象都将被清除


  • 我应该澄清一下。我相信,即使Test2和Test3实例仍然引用引用返回实例的计算实例,它们也会从ViewModel“main program”中分离出来,作为一个组进行垃圾收集。这有意义吗?我发布了()这促使Michael最好回答这个问题。我认为它可能值得分享,因为它可能会提供更多的见解和背景。您可能想知道为什么test3.3使用的是“self”,而不是“self.one”和“self.two”。这是因为在js中,self.one()实际上就是self['one']()。谢谢你的回答。我相信您所说的是正确的,但我不确定这是否回答了为什么对象Test2和Test3不是GC'd。我已经使用
    delete this.test2
    delete this删除了对viewmodel中创建的实例的引用。test3
    是否应该是实例对象对于GC是孤立的,即使它们具有内部引用循环?:)垃圾收集器无法处理循环引用。你可以用谷歌搜索这个主题。删除this.test2仅删除test2 hold by viewModel对象的引用。此答案是错误的。看看我提供的答案。我很高兴看到Michael对js mark和sweep的解释。Javascript的根对象架构师可以很容易地找到树中对象的依赖性。我不知道。谢谢你的回答。This.one和This.two是在任何时候都可以返回布尔值的函数。三是具有dispose function属性的computed。我不认为这是我的问题。请参阅:和状态不可访问的对象被清除,并且在拍摄快照时运行GC。不过,我明天会调查你的答案。另外,我关于谷歌集团的问题只是与此相关。不是同一个问题,但我将根据这个答案进行调查。但现在是凌晨2点45分,我应该睡着了。谢谢谢谢你的链接。我已将其添加到答案中。谢谢Michael,我确认Test2消失