I've been trying to track down any issues with garbage collection within my application code. I've stripped it down to pure knockout code and there seems to be an issue collecting created objects depending on how a computed property is created.
Please see the following JS fiddle: http://jsfiddle.net/SGXJG/
Please see the following code for the 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());
And here are the three tests classes:
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();
};
}
The difference being that Test1 'three' computed does not use this or self to reference the 'one' & 'two' observable properties. Can someone explain what's happening here? I guess there's something in the way the closures contain the object reference but I don't understand why
Hopefully I've not missed anything. Let me know if I have and many thanks for any responses.
Chrome, like all modern browsers, uses a mark-and-sweep algorithm for garbage collection. From MDN:
This algorithm assumes the knowledge of a set of objects called roots (In JavaScript, the root is the global object). Periodically, the garbage-collector will start from these roots, find all objects that are referenced from these roots, then all objects referenced from these, etc. Starting from the roots, the garbage collector will thus find all reachable objects and collect all non-reachable objects.
The garbage collector doesn't run right away, which is why you still see the objects in Chrome's snapshot even though you've dereferenced them (edit: As mentioned here, running the heap snapshot first runs the garbage collector. Possibly it's not processing everything and so doesn't clear the objects; see below.)
One thing that seems to generally trigger the garbage collector is to create new objects. We can verify this using your example. After going through the steps in your question, click on "Make1" and take another heap snapshot. You should see that Test2 is gone. Now do it again, and you'll see that Test3 is also gone.
Some more notes:
Chrome doesn't clean up everything during garbage collection. "[Chrome]...processes only part of the object heap in most garbage collection cycles." (google.com) Thus we see that it takes a couple runs of the garbage collector to clear everything.
As long as all external references to a object are cleared, the object will be cleaned up by the garbage collector eventually. In your example, you can even remove the dispose
calls, and all the objects will be cleared.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With