在KnockoutJS中更改模型数据时Javascript内存泄漏

时间:2013-11-18 13:44:35

标签: javascript knockout.js memory-leaks

我们正在使用KnockoutJS构建一个相当大的单页应用程序作为“数据处理程序”。

问题在于,在更改模型数据时,旧模型不会被垃圾收集器处理(看起来如此)。

该应用程序有大约12种不同的模型,具有计算的obserables,你可以检索与...的关系。

在ViewModel中,我为每个模型都有一个可观察的数组。让我们说我用100个模型实例填充一个数组。当我后来想要更改这100到100个不同的实例时,内存会增长并且永远不会下降(我正在使用Chrome并在Windows任务管理器中检查)。

应用程序非常复杂但我会给出一些我正在做的例子(代码被复制并简化为仅显示一些示例,一些代码可能看起来很奇怪,但可能是因为我删除了命名空间和其他的东西)。

视图模型:

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
    };

})();

部门模型

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;

};

地址模型

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;

};

还有更多的代码,但我正在寻找的是一种开始发现泄漏的方法(我已经尝试找几个小时了)。我的例子中有什么东西可以导致它吗?或者有其他人有过这种问题吗?该应用程序还有多个自定义绑定,但我试图删除使用绑定的HTML,似乎“原始”javascript对象占用了内存。

如果我要更改可观察的&amp;将模型中的变量计算为返回静态值的函数,这些函数似乎处理了对象。

1 个答案:

答案 0 :(得分:4)

每个computed'sDepartment中的Address将保留对viewmodel的订阅,直到您更换部门和地址为止。例如,我希望这会泄漏内存:

ViewModel.UpdateData({ departments: [ /* some new departments */ ] });
ViewModel.UpdateData({ departments: [ /* some new departments */ ] });
ViewModel.UpdateData({ departments: [ /* some new departments */ ] });
ViewModel.UpdateData({ departments: [ /* some new departments */ ] });
ViewModel.UpdateData({ departments: [ /* some new departments */ ] });
ViewModel.UpdateData({ departments: [ /* some new departments */ ] });
ViewModel.UpdateData({ departments: [ /* some new departments */ ] });
ViewModel.UpdateData({ departments: [ /* some new departments */ ] });

在这种情况下,所有“旧”部门仍将绑定到您的ViewModel.Addresses。更改Addresses应该释放内存:

ViewModel.UpdateData({ addresses: [ /* some new addresses */ ]});
// all the old departments should get GC'd now.

解决此问题的一种方法是修改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);
        });

        // dispose of the computeds in the old departments
        ko.utils.arrayForEach(_departments(), function (d) { d.Addresses.dispose(); });
        _departments(mappedData);
    }

    if (typeof data.addresses != 'undefined') {
        var mappedData = ko.utils.arrayMap(data.addresses , function(item) {
            return new Address(item);
        });
        // dispose of the computeds in the old addresses
        ko.utils.arrayForEach(_addresses(), function (a) { a.Department.dispose(); });
        _addresses (mappedData );
    }
};

return {
    departments: _departments,
    addresses: _addresses,
    UpdateData: UpdateData
};

})();

阅读Knockout Computed Documentation。具体滚动到底部并阅读dispose的文档。如果要删除它们,请确保您创建的任何计算的dispose,但是它们所依赖的可观察量不会被删除。