We're building a fairly large one-page-application with KnockoutJS as "data-handler".
The problem is that when changing model-data, the old models isn't disposed by the garbage collector (as it seems).
The application has about 12 different models with computed obserables that you can retreive relations with.
In the ViewModel I have a observable array for each of the model. Lets say I fill a array with 100 instances of a model. When I later want to change these 100 to 100 different instances the memory grows and it never goes down (I'm using Chrome and checking the in the Windows Task Manager).
The application is quite complex but I'll give some examples of what I'm doing (code is copied and simplified to only show some examples, some of the code might seem to be strange but its problably because I removed namespacing and other stuff).
ViewModel:
var ViewModel = (function () {
var _departments = ko.observableArray(),
_addresses = ko.observableArray();
var UpdateData = function (data) {
if (typeof data.departments != 'undefined') {
var mappedData = ko.utils.arrayMap(data.departments, function(item) {
return new Department(item);
});
_departments(mappedData);
}
if (typeof data.addresses != 'undefined') {
var mappedData = ko.utils.arrayMap(data.addresses , function(item) {
return new Address(item);
});
_addresses (mappedData );
}
};
return {
departments: _departments,
addresses: _addresses,
UpdateData: UpdateData
};
})();
Department model
var Department = function (data) {
var Department = {
Id: ko.observable(data.ClusterId),
Number: ko.observable(data.Number),
Name: ko.observable(data.Name)
};
var Addresses = ko.computed(function () {
return ko.utils.arrayFilter(ViewModel.addresses(), function (address) {
return address.DepartmentId() === Department.Id();
}).sort(function (a, b) {
return a.Number() < b.Number() ? -1 : 1;
});
}, Department);
var Update = function (data) {
Department.Id(data.Id);
Department.Number(data.Number);
Department.Name(data.Name);
};
$.extend(Department, {
Addresses: Addresses,
Update: Update
});
return Department;
};
Address model
var Address = function (data) {
var Address = {
Id: ko.observable(data.Id),
Number: ko.observable(data.Number),
Text: ko.observable(data.Text),
DepartmentId: ko.observable(data.DepartmentId)
};
var Department = ko.computed(function () {
return ko.utils.arrayFirst(ViewModel.departments(), function (item) {
return item.Id() == Address.DepartmentId();
});
}, Address);
var Update = function (data) {
Address.Id(data.Id);
Address.Number(data.Number);
Address.Text(data.Text);
Address.DepartmentId(data.DepartmentId);
};
$.extend(Address, {
Department: Department,
Update: Update
});
return Address;
};
There are alot more code but what I'm looking for is a way to start finding the leak (I have tried to find it for some hours now). Is there something in my example that could cause it? Or has anyone else had this kind of problem? The app also has multiple custom bindings but I've tried to remove the HTML that uses the bindings and it seems that the "raw" javascript object is what taking up the memory.
If I'm changing the observable & computed variables in the models to functions that return a static value the objects seems to be disposed.
The
computed's
within eachDepartment
andAddress
will keep their subscription to the viewmodel until you swap out both departments and addresses. For example, I would expect this to leak memory:In this case, all of the "old" departments will still be bound to your
ViewModel.Addresses
. Changing out theAddresses
should then release the memory:One way to resolve this is to modify your
ViewModel
to dispose of the old objects before removing them:Read the Knockout Computed Documentation. Specifically scroll to the bottom and read the documentation for
dispose
. Make sure youdispose
of any computeds you create if they are being removed but the observables they are depending upon are not being removed.