Angular 1.x memory leaks and cleanup

Angular cleans up and unbinds listeners and watches on scope and also unbinds directive elements (so you don't need to worry about those e.g. calling element.off() in a directive destroy).
"Listeners registered to scopes and elements are automatically cleaned up when they are destroyed," 
- ok so you don't need to unbind listeners from scope or element

But long running timeouts/intervals should be cleaned up on destroy. Short running timeouts can be assumed to be safe (completed before user leaves that code).
"Any listeners that we manually add or intervals or timeouts that are currently executing need to be cleaned up in $destroy event"
Whats a long running interval/timeout? I'd say 500ms and over should be explicitly released.

Directives can be a source of leaks.
Any listeners to service layers or to other DOM elements must be explicitly unbound on directive destroy (directive scope gets destroy event, so does the element).
"...but if you registered a listener on a service, or registered a listener on a DOM node that isn't being deleted, you'll have to clean it up yourself or you risk introducing a memory leak."

Some examples where you must cleanup yourself when writing directives:

  • listeners added by directive to services
  • listeners on dom not automatically cleaned up by angular e.g. "If you attach an event-listener in your directive using a JQuery plugin, the latter would keep a reference to your DOM even after Angular deletes its own reference to the DOM"
  • listeners added to dom elements that are not the directive element such as adding listeners to window or document e.g. $( window ).on( "resize.Viewport", ... ) OR $(document).on('click', ....). This was a leak we found and fixed in our code.
  • plugins such as calling a plugin from your directive e.g. element.datetimepicker(); Not sure about this but best to cleanup and be safe.
  • if you don't unbind manually (e.g. calling .off()) in such cases then there are two problems 
    • 1. your directive and its scope will not be freed up for garbage collection (because the there's still a reference to the callback) and 
    • 2. the listener is "out there" possibly responding and taking actions on events which can cause unexpected bugs (i.e. a rogue listener). As you go in and out of such a directive more and more rogue listeners are added and active.
Note: its assumed you're only writing dom manipulation in directives, not in controllers/services. If thats not the case (it should be) then the list above also applies to wherever dom manipulation happens.

RootScope listeners can be a source of leaks because rootScope is not destroyed until the web app is destroyed. If you add a listener to rootScope realize its there forever (at least while your web app is running) until you explicitly remove it...so ask yourself is that what you really want? We've fixed at least 2 such rootScope listener leaks in our app already. Prefer to add listeners to controller scope which is cleaned up automatically by angular. If you do add a listener to rootScope then have a plan for when to remove it.


Unit tests: when you new a scope, once done please destroy it so to free the scope to be garbage collected.

Comments

Popular posts from this blog

deep dive into Material UI TextField built by mui

angular js protractor e2e cheatsheet

react-router v6.4+ loaders, actions, forms and more