如何在Angular应用程序中捕获内存泄漏?

我有一个用AngularJS编写的Web应用程序,它基本上将API调查为两个端点。 所以,每一分钟它轮询,看看有什么新的东西。

我发现它有一个小的内存泄漏,我已经尽了最大的努力去find它,但我无法做到这一点。 在这个过程中,我设法减less了我的应用程序的内存使用情况,这非常棒。

没有做任何事情,每一个民意调查,你可以看到在内存使用(这是正常的)峰值,然后它应该下降,但它一直在增加。 我已经将数组的清理从[]更改为array.length = 0 ,我想我确定引用不会保留,所以它不应该保留任何这些。

我也试过这个: https : //github.com/angular/angular.js/issues/1522

但没有任何运气…

所以,这是两堆之间的比较:

内存堆

大部分泄漏似乎来自(数组),如果我打开,是由API调用parsing返回的数组,但我相信他们没有被存储:

这基本上是这样的结构:

 poll: function(service) { var self = this; log('Polling for %s', service); this[service].get().then(function(response) { if (!response) { return; } var interval = response.headers ? (parseInt(response.headers('X-Poll-Interval'), 10) || 60) : 60; services[service].timeout = setTimeout(function(){ $rootScope.$apply(function(){ self.poll(service); }); }, interval * 1000); services[service].lastRead = new Date(); $rootScope.$broadcast('api.'+service, response.data); }); } 

基本上,假设我有一个sellings服务,那么这就是servicevariables的价值。

那么,主要观点是:

 $scope.$on('api.sellings', function(event, data) { $scope.sellings.length = 0; $scope.sellings = data; }); 

而视图确实有一个ngRepeat ,根据需要呈现。 我花了很多时间试图自己解决这个问题,而我却做不到。 我知道这是一个很难的问题,但是,有没有人有任何想法如何跟踪这个?

编辑1 – 添加承诺展示:

这是两个服务使用的函数makeRequest

 return $http(options).then(function(response) { if (response.data.message) { log('api.error', response.data); } if (response.data.message == 'Server Error') { return $q.reject(); } if (response.data.message == 'Bad credentials' || response.data.message == 'Maximum number of login attempts exceeded') { $rootScope.$broadcast('api.unauthorized'); return $q.reject(); } return response; }, function(response) { if (response.status == 401 || response.status == 403) { $rootScope.$broadcast('api.unauthorized'); } }); 

如果我注释$scope.$on('api.sellings')部分,泄漏仍然存在,但下降到1%。

PS:我正在使用最新的Angular版本

编辑2 – 在图像中打开(arrays)树

在这里输入图像说明

这就是这样的一切,所以这是相当无用的imho 🙁

此外,这里有4堆报告,所以你可以玩自己:

https://www.dropbox.com/s/ys3fxyewgdanw5c/Heap.zip

编辑3 – 响应@zeroflagL

编辑指令,对泄漏没有任何影响,尽pipe闭包部分似乎更好,因为它没有显示jQuerycaching的东西?

没有更多的泄漏?

该指令现在看起来像这样

 var destroy = function(){ if (cls){ stopObserving(); cls.destroy(); cls = null; } }; el.on('$destroy', destroy); scope.$on('$destroy', destroy); 

对我来说, (array)部分似乎正在发生什么。 投票之间还有3个新的堆 。

答案是caching。

堆快照分析

我不知道是什么,但是这个东西在增长。 这似乎与jQuery有关。 也许这是jQuery元素caching。 在每次服务调用之后,您是否有机会在一个或多个元素上应用jQuery插件?

更新

问题是HTML元素被添加,使用jQuery处理(例如通过popbox插件),但是根本不会被删除,或者没有被jQuery删除。 在这种情况下处理意味着像添加事件处理程序。 如果jQuery知道元素已被删除,caching对象中的条目(不pipe它是什么)只会被删除。 这是元素必须与jQuery删除。

更新2

目前还不清楚为什么caching中的这些条目还没有被删除,因为angular被包含在jQuery中。 但他们已经通过评论中提到的插件添加,并包含事件处理程序和数据。 AFAIK Antonio已经改变了插件代码来解除绑定事件处理程序,并删除插件的destroy()方法中的数据。 这最终消除了内存泄漏。

修复内存泄漏的标准浏览器方法是刷新页面。 而JavaScript的垃圾收集是一种懒惰,可能在这个银行。 而且由于Angular通常是SPA,所以浏览器从来没有机会刷新。

但是我们有一件有利于我们的事情: Javascript主要是一种自上而下的层次语言。 而不是从下往上search内存泄漏,我们可能可以从上往下清除它们。

因此,我想出了这个解决scheme,它可以工作,但可能会或可能不会100%有效,具体取决于您的应用程序。

主页

典型的Angular应用程序主页由一些Controller和ng-view 。 喜欢这个:

<div ng-controller="MainController as vm"> <div id="main-content-app" ng-view></div> </div>

控制器

然后为了刷新控制器中的应用程序,从上面的代码中得到MainController,我们冗余地调用了jQuery的.empty()和Angular的.empty(),以确保清除了任何交叉库引用。

 function refreshApp() { var host = document.getElementById('main-content-app'); if(host) { var mainDiv = $("#main-content-app"); mainDiv.empty(); angular.element(host).empty(); } } 

并在路由开始之前调用上述内容,模拟页面刷新:

 $rootScope.$on('$routeChangeStart', function (event, next, current) { refreshApp(); } ); 

结果

这是一个“刷新浏览器types的行为”,清除DOM,希望有任何泄漏hacky方法。 希望能帮助到你。