在KnockoutJS中更改模型数据时Javascript内存泄漏 [英] Javascript memory leak when changing model data in KnockoutJS

查看:51
本文介绍了在KnockoutJS中更改模型数据时Javascript内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



该应用程序有大约12种不同的模型,带有计算的obserables,你可以用它来回复关系。



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



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



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

返回{
部门:_departments,
地址:_addresses,
UpdateData:UpdateData
};

})();

部门型号

  var Department = function(data){

var Department = {
Id:ko.observable(data.ClusterId),
编号:ko.observable(data.Number),
名称: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;
});
},部门);

var Update = function(data){
Department.Id(data.Id);
Department.Number(data.Number);
Department.Name(data.Name);
};

$ .extend(部门,{
地址:地址,
更新:更新
});

返回部门;

};

地址型号

  var地址=功能(数据){

var地址= {
Id:ko.observable(data.Id),
编号:ko.observable(data.Number),
文本: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();
});
},地址);

var Update = function(data){
Address.Id(data.Id);
Address.Number(data.Number);
Address.Text(data.Text);
Address.DepartmentId(data.DepartmentId);
};

$ .extend(地址,{
部门:部门,
更新:更新
});

返回地址;

};

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



<如果我正在改变观察力和观察力度将模型中的变量计算为返回静态值的函数,这些函数似乎处理了对象。

解决方案

部门和地址中的> computed 将保留对viewmodel的订阅,直到你交换两个部门和地址。例如,我希望这会泄漏内存:

  ViewModel.UpdateData({departments:[/ *一些新部门* / ]}); 
ViewModel.UpdateData({departments:[/ *一些新部门* /]});
ViewModel.UpdateData({departments:[/ *一些新部门* /]});
ViewModel.UpdateData({departments:[/ *一些新部门* /]});
ViewModel.UpdateData({departments:[/ *一些新部门* /]});
ViewModel.UpdateData({departments:[/ *一些新部门* /]});
ViewModel.UpdateData({departments:[/ *一些新部门* /]});
ViewModel.UpdateData({departments:[/ *一些新部门* /]});

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

  ViewModel.UpdateData ({地址:[/ *一些新地址* /]}); 
//所有旧部门现在应该得到GC。

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

//处理旧部门中的计算结果
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) {
返回新地址(item);
});
//处置旧地址中的计算结果
ko.utils.arrayForEach(_addresses(),function(a){a.Department.dispose(); });
_addresses(mappedData);
}
};

返回{
部门:_departments,
地址:_addresses,
UpdateData:UpdateData
};

})();

阅读淘汰计算机文档。具体滚动到底部并阅读 dispose 的文档。确保你在处置你创建的任何计算机如果它们被删除但是它们所依赖的可观察数据没有被删除。


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 each Department and Address will keep their subscription to the viewmodel until you swap out both departments and addresses. For example, I would expect this to leak memory:

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 */ ] });

In this case, all of the "old" departments will still be bound to your ViewModel.Addresses. Changing out the Addresses should then release the memory:

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

One way to resolve this is to modify your ViewModel to dispose of the old objects before removing them:

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

})();

Read the Knockout Computed Documentation. Specifically scroll to the bottom and read the documentation for dispose. Make sure you dispose of any computeds you create if they are being removed but the observables they are depending upon are not being removed.

这篇关于在KnockoutJS中更改模型数据时Javascript内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆