13
votes

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.

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/396dennis
Hi! Which version of nvd3 you use?Artemis
@Artemis v1.1.15-betaPierre

3 Answers

2
votes

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.

2
votes

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).

0
votes

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.