CodePrimate
CodePrimate

Reputation: 6666

Why are scopes not destroyed even after $destroy is triggered?

I have made a directive that, when clicked, creates a dialog that is appended to the body using jQuery. The problem is that when the dialog is closed, the scopes are never properly cleaned up. As shown in the picture below 167 ChildScopes are preserved. Which matches the amount of items in the dialog which includes the ng-repeat directive.

enter image description here

I attempted to create an extremely simple version of the scenario on Plnkr. To my surprise the scopes ARE in fact being removed on each close in the Plnkr. So something, somewhere in production is causing the scopes to stay alive even after $destroy has been called.

link: ($scope, $element, $attr) ->
  $element.on 'click', () ->
      $scope.$apply () ->
        child = $scope.$new()
        template = """<span ng-controller="ListCtrl">...List dialog things...</span>"""
        compiledTemplate = $compile(template)(child)
        container = containers.createDialogContainer($element)
        container.append(compiledTemplate)

        #cleanup
        $scope.closeWidget = () ->
          container.trigger("container_close")
          return

        container.on "container_close", ()->
          child.$apply () ->
            child.$destroy()
          return

So here is my question:

What can cause a scope to stay alive even after $destroy has been called, triggered and garbage collection performed?

For obvious reasons I cannot show you our production code. However the directive in the Plnkr matches the one im debugging sufficiently.

Upvotes: 15

Views: 4278

Answers (3)

btm1
btm1

Reputation: 3856

The only thing I can think of is that if you somewhere have a global function or function outside of the controller or directive that references a method inside of the scope for the directive it will infact keep that scope alive for the duration of the application.

Upvotes: 0

goonerify
goonerify

Reputation: 1748

Closure functions can cause a functions Activation object to stay alive even after the scope has been "destroyed". For instance you might have inner functions still referencing variable objects in the functions whose scope you are trying to destroy.

nullifying the reference variables would be the best option as opposed to delete

Upvotes: 0

Pieter Herroelen
Pieter Herroelen

Reputation: 6066

In general, a scope (or any other JS object) can not be cleaned up by the GC if it is still accessible by another JS object.

In practice, in an Angular project that also uses JQuery, this is most likely caused by:

  • an Angular service, controller, scope or some other object still having a reference to your scope object
  • a reference to your object still exists through a DOM element, probably through an event listener. The DOM element might not be GC-able itself because it is still in the cache of JQuery

Your example, for instance, creates a memory leak.

From your code:

 $scope.removeDialog = () ->
    console.log "closing"
    child.$destroy()
    $('.listshell').remove()
    return

You do not set child to null so $scope.removeDialog still has access to the scope object referenced by the child variable. Therefore, this object can not be GC'ed.

NB: It seems to me that it would be more appropriate to put removeDialog on the child scope. Now your example only works because the child scope is not isolated.

Upvotes: 5

Related Questions