Leaking $scope when method on scope references $scope itself

653 views
Skip to first unread message

Alex Shnayder

unread,
Dec 23, 2013, 7:41:58 AM12/23/13
to ang...@googlegroups.com
I got a really strange memory leak going on in our application. We have rather big angular (1.1.4) based app, and we noticed huge memory leak.
We are using ui-router and every time we switch between big states (which have lots of JS and DOM in them) we leak about 5 MB of JavaScript memory, and big amounts of DOM as well.
After 10 transitions between states our app takes about 1 GB of memory on Chrome.

After rather painful investigation I came to the following conclusions for which I would really like to get a second opinion and possible some kind of explanation (because I can't wrap my head around it)
  1. This is not related to ui-router in any way
  2. A cause looks like the leak of $scope for the root state from which we transition away.
  3. The leak is result of state root scope been alive after transition which keeps alive all the methods and watch expressions on that scope which in turn keep alive all the closures of those methods
    1. For controller this means that big chunks of that controller are keept alive
    2. For directives it means that any references they have to DOM elements are kept alive as they are in the closure of the watch expressions applied by the directive.
      A really good example for this is ui-if / ng-if which has reference to it's element which is kept alive, which in turn essentially keeps alive the entire DOM for our state as we have the ui-if at the root of the state.
  4. Unclear why, but $scope leaks when there are methods on scope that reference scope itself,
    like

    $scope.flipFlag = function(){ $scope.flag = !$scope.flag }

    If same code is written as 

    $scope.flipFlag = function(){ this.flag = !this.flag }

    Then it looks like scope does not leak.
  5. This issue reproduces both on angular 1.1.4 and on 1.2.5
I have been using Chrome DevTools heap dump (didn't find a good and easy way to get heap dump on other browsers, if there, please share) to detect this issue.

I have reproduction of this issue at http://plnkr.co/edit/JZbtZ13Ml1aDjNJug1gu?p=preview on top of angular 1.2.5
What we do in this reproduction is set nad unset ng-include to point to a template with a controller.
In the controller we have scope method that references $scope.

To see the leak, on reproduction page, after accessing it, take a heap dump and there on summary view filter by Child constructor.
You will see that there are two scopes leaking, with ids 004 and 005, where 005 is the scope for the controller injected in the view.html and 004 is it's prototype.
From what I can see 004 isn't released because it's held by 005 (as it's prototype).
And 005 isn't released, well here it's not so clear, from what I can see it's not released because it has a function that has it in it's closure / scope.

If the issue does not reproduce, play around with show and hide buttons a few times, make sure to hide as last step.

I'm not clear if this is an angular bug, or Chrome GC bug, any ideas and help will be much appreciated.

I do want to note that in angular.js in Scope.$destroy we have this

        // This is bogus code that works around Chrome's GC leak
        this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
            this.$$childTail = null;

So may be we need to clear not only $parent and so on, but all the properties owner by scope ?
But that won't help as well in all cases as from what I can see we don't actually run Scope.$destroy on each scope, we only broadcast to child scopes.

Help


Stephen Kawaguchi

unread,
Mar 1, 2014, 1:24:24 PM3/1/14
to ang...@googlegroups.com
Alex,
I've got a very similar issue. Mine seems to superficially be related to ng-include rather than ui-router. Have you had any luck tracking down the fix for this?

Thanks,
Stephen

Alex Shnayder

unread,
Mar 11, 2014, 7:15:32 PM3/11/14
to ang...@googlegroups.com
I have opened an issue for this at angular github, see details at https://github.com/angular/angular.js/issues/5527
Reply all
Reply to author
Forward
0 new messages