7

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

11
  • I guess you are using ngRoute and some custom directives inside your templates. Maybe you even have bindings to 3rd party libraries inside your directives. Bottom line, you should clean up on $destroy events ( and if you are not lucky than it's one of these jQuery plugins that is leaking). Let us see some code if you want more help. Commented Jan 22, 2014 at 12:20
  • @IlanFrumer you are correct about the usage of ng-route and other plugins. for the dropdown, i'm using a jQuery+Bootstrap plugin for the retailer dropdown called Bootstrap-select. For that, i've used a custom directive which triggers at $last of ng-repeat. Also, for the dates, i'm using a jQuery datepicker. Can you help me understand cleaning up on $destroy event? I'm not doing anything of that sorts. Commented Jan 22, 2014 at 12:44
  • You can contact me by email if you cannot share the code here. Commented Jan 22, 2014 at 12:46
  • @IlanFrumer i just updated my answer with some code snippets. Let me know if you'd like to view more code, then I'll send it through email. It's nothing very secretive, it'll just be a bit verbose to put it all here :] Commented Jan 22, 2014 at 13:04
  • comment off each plugin / directive and check if a memory leak still happens so we can focus on which one causes it. Commented Jan 22, 2014 at 13:23

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.