如何在 Angular 应用程序中捕获内存泄漏? [英] How to catch memory leaks in an Angular application?

查看:55
本文介绍了如何在 Angular 应用程序中捕获内存泄漏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用 AngularJS 编写的 web 应用程序,它基本上将 API 轮询到两个端点.因此,它每分钟都会轮询以查看是否有任何新内容.

我发现它有一个小的内存泄漏,我已尽力找到它,但我无法做到.在这个过程中,我设法减少了应用程序的内存使用量,这很棒.

如果不做任何其他事情,每次轮询您都可以看到内存使用量激增(这是正常的),然后它应该会下降,但它总是在增加.我已经将数组的清理从 [] 更改为 array.length = 0 并且我想我确信引用不会持续存在,所以它不应该保留任何这些.

我也试过这个:>

编辑 3 - 回应@zeroflagL

编辑指令,对泄漏没有任何影响,尽管闭包部分似乎更好,因为它没有显示 jQuery 缓存内容?

指令现在看起来像这样

var destroy = function(){如果(CLS){停止观察();cls.destroy();cls = 空;}};el.on('$destroy', 销毁);scope.$on('$destroy', destroy);

对我来说,发生的事情似乎发生在 (array) 部分.轮询之间还有 3 个新堆.

解决方案

答案就是缓存.

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

更新

问题是 HTML 元素被添加,用 jQuery 处理(例如通过 popbox 插件),但要么根本没有删除,要么没有用 jQuery 删除.在这种情况下处理意味着添加事件处理程序之类的东西.缓存对象中的条目(无论它用于什么)只有在 jQuery 知道元素已被删除时才会被删除.那就是必须使用 jQuery 删除元素.

更新 2

不清楚为什么缓存中的这些条目没有被删除,因为 angular 应该使用 jQuery,当它被包含时.但是它们是通过评论中提到的插件添加的,并包含事件处理程序和数据.AFAIK Antonio 已更改插件代码以取消绑定事件处理程序并删除插件的 destroy() 方法中的数据.这最终消除了内存泄漏.

I have a webapp written in AngularJS which basically polls an API to two endpoints. So, every minute it polls to see if there is anything new.

I discovered that it has a small memory leak and I've done my best to find it but I'm not able to do it. In the process I've managed to reduce the memory usage of my app, which is great.

Without doing anything else, every poll you can see a spike in the memory usage (that's normal) and then it should drop, but it's always increasing. I've changed the cleaning of the arrays from [] to array.length = 0 and I think I'm sure that references don't persist so it shouldn't be retaining any of this.

I've also tried this: https://github.com/angular/angular.js/issues/1522

But without any luck...

So, this is a comparison between two heaps:

Most of the leak seems to come from (array) which, if I open, are the arrays returned by the parsing of the API call but I'm sure they're not being stored:

This is basically the structure:

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

Basically, let's say I have a sellings service so, that would be the value of the service variable.

Then, in the main view:

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

And the view does have an ngRepeat which renders this as needed. I spent a lot of time trying to figure this out by myself and I couldn't. I know this is a hard issue but, do anyone have any idea on how to track this down?

Edit 1 - Adding Promise showcase:

This is makeRequest which is the function used by the two services:

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

If I comment out the $scope.$on('api.sellings') part, the leakage still exists but drops to 1%.

PS: I'm using latest Angular version to date

Edit 2 - Opening (array) tree in an image

It's everything like that so it's quite useless imho :(

Also, here are 4 heap reports so you can play yourself:

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

Edit 3 - In response to @zeroflagL

Editing the directive, didn't have any impact on the leak although the closure part seems to be better since it's not showing jQuery cache things?

The directive now looks like this

var destroy = function(){
  if (cls){
    stopObserving();
    cls.destroy();
    cls = null;
  }
};

el.on('$destroy', destroy);
scope.$on('$destroy', destroy);

To me, it seems that what's happening is on the (array) part. There is also 3 new heaps in between pollings.

解决方案

And the answer is cache.

I don't know what it is, but this thing grows. It seems to be related to jQuery. Maybe it's the jQuery element cache. Do you by any chance apply a jQuery plugin on one or more elements after every service call?

Update

The problem is that HTML elements are added, processed with jQuery (e.g. via the popbox plugin), but either never removed at all or not removed with jQuery. To process in this case means stuff like adding event handlers. The entries in the cache object (whatever it is for) do only get removed if jQuery knows that the elements have been removed. That is the elements have to be removed with jQuery.

Update 2

It's not quite clear why these entries in the cache haven't been removed, as angular is supposed to use jQuery, when it's included. But they have been added through the plugin mentioned in the comments and contained event handlers and data. AFAIK Antonio has changed the plugin code to unbind the event handlers and remove the data in the plugin's destroy() method. That eventually removed the memory leak.

这篇关于如何在 Angular 应用程序中捕获内存泄漏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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