How to avoid memory leaks using angularjs-nvd3-directives
Asked Answered
A

3

13

I'm working on an angularjs application using angularjs-nvd3-directives to render charts.

After a check with Chrome Developer Tools, I detected some memory leaks linked to the charts. When the user navigates through different views containing charts the memory is never fully released.

I'm already doing some cleanup on the graphs controllers:

$scope.$on('$destroy', function() {
  d3.select( '#exampleId' ).remove();
  d3.select( '#exampleId2' ).remove();
  ...
});

And on the routeChange event:

myApp.run(function($rootScope, $templateCache) {
  //try to clear unused objects to avoid huge memory usage
  $rootScope.$on('$routeChangeStart', function(event, next, current) {
    if (typeof(current) !== 'undefined'){
      //destroy all d3 svg graph
      d3.selectAll('svg').remove();
      nv.charts = {};
      nv.graphs = [];
      nv.logs = {};
    }
  });
});

When I remove the charts from my app, the memory usage always goes back to the initial value.

With the graph: with graph Whithout: without

Is there any other way to release memory generated by those charts ?

jsfiddle to demonstrate the issue.

Analiese answered 18/7, 2014 at 9:14 Comment(3)
I have no answer but I'm also eager to find a way how to destroy the charts on a state/route change. This problem is discussed here: github.com/novus/nvd3/pull/396Alga
Hi! Which version of nvd3 you use?Nettienetting
@Nettienetting v1.1.15-betaAnaliese
N
2

You might forget to remove window resize listeners.

angularApp.run(function($rootScope) {
  $rootScope.$on('$routeChangeStart', function(event, next, current) {
    if (typeof(current) !== 'undefined'){
        //destroy d3 stuff 
        window.nv.charts = {};
        window.nv.graphs = [];
        window.nv.logs = {};

        // and remove listeers for onresize. 
        window.onresize = null;
    }
  });
}); 

Also you can try removing whole svg element but it doesn't seem to be the best way.

Nettienetting answered 10/9, 2014 at 12:4 Comment(2)
window.onresize = null; is a good point, it works fine on the jsfiddle example. However, with more complex graph, it seems there are still memory leaks linked to nvd3 graphs. I guess you'll have the reward if I don't have any other answers.Analiese
You can also try to use d3 api directly - sometimes you can gain some performance increase. Nvd3 is just wrapper for d3 apis. You can look here - github.com/d3/d3-plugins.Nettienetting
B
2

There is a similar issue at the github: https://github.com/cmaurer/angularjs-nvd3-directives/issues/193

As I explained there the following worked better:

  $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
angular.element(document.body.querySelectorAll('.nvd3')).remove();

This solves the SVG memory leaks. But still there are some memory leaks on the data side (Array).

Bilk answered 6/1, 2015 at 12:13 Comment(3)
Thanks for the input, I don't use nvd3 anymore but this is very interesting. It looks like d3.selectAll('svg').remove(); but in the angular way.Analiese
nope. I already had d3.selectAll('svg').remove(); which did not work. By the way, I am combining both of these codes(you wrote and mine) on the broadcast call.Bilk
Just to inform d3 user, I'm using d3.select( "#"+attrs.id ).remove(); in my custom directives on the $destroy event. It's working for me. There are still leaks but less than before.Analiese
S
0

I recommend that you move your graph to your own directives that will hold the nvd3 directives on their templates and listen on each directive for scope.

$destroy also destroy the element on this event.

Controllers should retrieve the data and assign them to the directive.

You may be want to listen to the $routeChangeStart on the directive, so the cleaning will be encapsulated on the part that use the data. This way you will avoid duplicate code.

I use this techniques to clean my directives that use modals so i don't have duplicate events listeners or ids.

Sextet answered 29/10, 2014 at 10:14 Comment(1)
I'm now using my own directives, without nvd3 just D3. This way I know exactly what to destroy on scope.$destroy. I solved 95% of the leak like this.Analiese

© 2022 - 2024 — McMap. All rights reserved.