Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angularjs memory consumption issue

Recently I had the opportunity of building a new web application, and thought of trying out Angular to get a good understanding of it. So yeah, I'm fairly new to this framework.

After understanding the nuances of the framework, I found it surprisingly easy to work with. Everything about my experience had been just great, until users started reporting the utterly laggy performance of the application.

The application is fairly simple—it's got 2 screens. One which shows a list of deals, and another where users can add/edit deal information—this second page is a simple form expecting the user to enter deal related information. It looks like this:

enter image description here

The outlined sections are rendered using ng-repeat. The retailers list has some 530 entries whereas the brand list has about 400 entries.

After a bit of profiling, I figured out that visiting this second form screen would keep on increasing the memory consumption of the browser. The first screen doesn't have any such effect. I simply toggled between the first screen and this second form screen, and found that every time this screen would get loaded, the memory consumption would spike by 50-75 MB. Eventually, the browser would just freeze up. Here's how the memory profile looks:

enter image description here

As you can see, the consumption keeps going up, and there's no sign of any GC! Every spike in the node count and memory trace correspond to a visit to the second form-based screen.

Now I have already checked out a whole lot of issues around angular and memory consumption, but each of them mentions that the $scope for any of the views will get removed when a new view loads. The DOM node count certainly doesn't indicate such a thing for me :/

I also came across 2 important points related to the usage of ng-repeat:

  1. Avoid invocation of any function within the ng-repeat directive.
  2. Don't have a two-way binding using ng-model within a ng-repeat directive.

Both of these I've avoided in the second screen, and yet, the memory consumption is going through the roof.

My question might seem to be yet another memory related question w.r.t angular, but I've really tried to get some sort of closure on this and haven't found one.

Would really appreciate any assistance on this, as my decision to progress with the usage of angular for the rest of the portal hinges on solving this issue.

Thanks for reading!

Update 1

Based on Ilan's suggestion, let me add that I make use of 2 plugins for rendering the dropdown and the implementing the date-picker.

For the dropdown, i'm using Bootstrap-select and for the date-picker, I'm using Bootstrap-datepicker.

For bootstrap-select to work, I had to write a custom directive which fired a broadcast on the $last event of ng-repeat. It looks something like this:

.directive('onFinishRender', function($timeout) {
    return {
        restrict : 'A',
            link : function(scope, element, attr) {
                if (scope.$last === true) {
                    $timeout(function() {
                        scope.$emit('ngRepeatFinished');
                     });
                 }
            }
      };
});

Then in the controller, I rely on this event to invoke the render for the dropdown plugin:

    $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
        $('#retailer').selectpicker('render');
    });

For the bootstrap-datepicker, I do not have to do such an elaborate thing, as I only need to wrap the date input field using JS.

Update 2

After turning off the plugins, the memory consumption reduces drastically. However, the problem of a leak still persists. Earlier, whenever the form view was getting loaded, the memory would spike by 50-60 MB. After turning off the plugins, it spikes by 25-35 MB. But as you can see below, the memory consumption keeps on piling up.

enter image description here

like image 475
anirvan Avatar asked Jan 22 '14 12:01

anirvan


1 Answers

I recently spent nights and days finding similar memory leaks as that of yours. These is no direct answer to your question. You will have to do the research but i can give you some pointers to finding the leak.

  1. Don't use any other plugin in your chrome browser except for developer toolbar when debugging leak.
  2. Timeline is good to figure out that there is a leak but to actually see the leak, use profiler tab. It runs a GC everytime you take a Heap Snapshot and gives you a clue if you improved or not.
  3. If you are seeing memory leak in MBs than it is coming from DOMElements. With the size of leaks that you mentioned, i can tell that your whole document is hanging as detached dom element because of one or two components in your page are not getting released and are still hanging as attached dom.
  4. Remove all the elements from your second page and do the switch to see if memory is increasing. If it does, first page has the leak otherwise do the same with second page.
  5. Once you have located which page has the leak, remove all component from that page and add them one by one to see when leak returns.

Hope these steps help you in some way. Also i have found that using $timeout in directive can cause leaks just in case it helps.

like image 81
nikhilgupta Avatar answered Oct 08 '22 00:10

nikhilgupta