Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

tooltip: memory leak on destroy #11133

Closed
pbininda opened this issue Feb 22, 2018 · 7 comments
Closed

tooltip: memory leak on destroy #11133

pbininda opened this issue Feb 22, 2018 · 7 comments
Assignees
Labels
has: Pull Request A PR has been created to address this issue P1: urgent Urgent issues that should be addressed in the next minor or patch release. resolution: fixed severity: memory leak Issues where a memory leak is triggered. severity: regression This issue is related to a regression type: bug
Milestone

Comments

@pbininda
Copy link

Bug, feature request, or proposal:

commit #11087 introduced a memory leak in md-tooltip

What is the expected behavior?

no memory leak

What is the current behavior?

memory leak in md-tooltip

CodePen and steps to reproduce the issue:

CodePen Demo which shows your issue:

https://codepen.io/pbininda/pen/vdrZZX

Detailed Reproduction Steps:
Step 1: No memory leaked
  • load the CodePen in Chrome and open chrome dev tools
  • Hit the "TOGGLE BUTTON" 10 times (making the ng-if shown true 5 times)
  • In the dev tools take a memory snapshot and search for "Canary"
  • No living instances of "Canary" are found.
Step 2: Trigger memory leak
  • load the CodePen in Chrome and open chrome dev tools
  • Hit the "TOGGLE BUTTON" 10 times (making the ng-if shown true 5 times)
    • Each time when the "BUTTON WITH TOOLTIP" is shown, hover over the "BUTTON WITH TOOLTIP" to show the "tooltip"
  • In the dev tools take a memory snapshot and search for "Canary"
  • 5 live instances of "Canary" are shown.

What is the use-case or motivation for changing an existing behavior?

We have a big application using angular-material. This memory leak keeps a huge chain of objects alive whenever a user hovers over a button.

Which versions of AngularJS, Material, OS, and browsers are affected?

Material version 1.1.7

Is there anything else we should know? Stack Traces, Screenshots, etc.

Reverting the commit #11087 fixes the problem.

Below is a screenshot of the heap snapshot showing 5 leaked instances of "Canary"

image

@fsmeier
Copy link
Contributor

fsmeier commented Feb 22, 2018

Are you sure it was the linked commit? - I do not see anything which could lead to memory leak.
But maybe we should take a look at #10651
Edit: but also there it does not look like something which could introduce missing memory cleanup

@pbininda
Copy link
Author

pbininda commented Feb 22, 2018

Looking at the retainers chain (see screenshot), it seems the onDomAdded callback function that is registered by the tooltip, keeps the $scope object alive through the closure.
Anyway, if I undo the changes of the linked commit, the leak goes away.

@fsmeier
Copy link
Contributor

fsmeier commented Feb 22, 2018

Ok, I can now reproduce the problem, but the problem seems like not the tooltip itself. If I replace the onDomAdded-function with the following i have still the leak.

onDomAdded: function() {
            var i = 1 + 3;
          }

Update of research: ANY function passed inside the config leads to this problem.

@fsmeier
Copy link
Contributor

fsmeier commented Feb 22, 2018

I still think the linked commit is not the problem!
The last two hours I tracked the problem down and I found some problems which just got visible whenever the config has ANY closures.

1. The config together with scope is cached in the trackedPanels-object with the following two lines:

  var panelRef = new MdPanelRef(this._config, this._$injector);
  this._trackedPanels[config.id] = panelRef;

There is some missbehavior with the trackedPanels and config anyway and I think the trackedPanels should handle the scope somehow. For me it is linked to #10894

  1. The config for the panel is stored as object-property but I do not see any reason why. At least it should free the config-values for the garbage-collector since it has the scope linked there.
delete this._config;

So in the end for now the fastest fix without loosing any at the moment working functionality is the following create method (just the changes described above changed):

MdPanelService.prototype.create = function(preset, config) {
  if (typeof preset === 'string') {
    preset = this._getPresetByName(preset);
  } else if (typeof preset === 'object' &&
      (angular.isUndefined(config) || !config)) {
    config = preset;
    preset = {};
  }

  preset = preset || {};
  config = config || {};

  // If the passed-in config contains an ID and the ID is within _trackedPanels,
  // return the tracked panel after updating its config with the passed-in
  // config.
  if (angular.isDefined(config.id) && this._trackedPanels[config.id]) {
    var trackedPanel = this._trackedPanels[config.id];
    angular.extend(trackedPanel.config, config);
    return trackedPanel;
  }

  // Combine the passed-in config, the _defaultConfigOptions, and the preset
  // configuration into the `_config`.
  this._config = angular.extend({
    // If no ID is set within the passed-in config, then create an arbitrary ID.
    id: config.id || 'panel_' + this._$mdUtil.nextUid(),
    scope: this._$rootScope.$new(true),
    attachTo: this._$rootElement
  }, this._defaultConfigOptions, config, preset);

  // Create the panelRef and add it to the `_trackedPanels` object.
  var panelRef = new MdPanelRef(this._config, this._$injector);
  // removing the following line fixes the multiple memory leak of the scope
  this._trackedPanels[config.id] = panelRef;

  // Add the panel to each of its requested groups.
  if (this._config.groupName) {
    if (angular.isString(this._config.groupName)) {
      this._config.groupName = [this._config.groupName];
    }
    angular.forEach(this._config.groupName, function(group) {
      panelRef.addToGroup(group);
    });
  }

  this._config.scope.$on('$destroy', angular.bind(panelRef, panelRef.detach));

 // adding the following line fixes the scope in the memory even if the tooltip is not there anymore
  delete this._config;

  return panelRef;
};

PS: I do not have time until the 5.3.2018 to fix the complete trackedPanels-messup which would fix this as well.

@Splaktar Splaktar added this to the 1.1.8 milestone Feb 26, 2018
@Splaktar Splaktar added type: bug needs: Pull Request P1: urgent Urgent issues that should be addressed in the next minor or patch release. labels Feb 26, 2018
@Splaktar Splaktar added the severity: memory leak Issues where a memory leak is triggered. label Mar 3, 2018
@Splaktar Splaktar changed the title memory leak in md-tooltip md-tooltip: memory leak Mar 3, 2018
@Splaktar Splaktar self-assigned this Mar 3, 2018
@Splaktar Splaktar added has: Pull Request A PR has been created to address this issue and removed needs: Pull Request labels Mar 3, 2018
@Splaktar
Copy link
Member

Splaktar commented Mar 3, 2018

Can someone please test #11145 to see if it solves this leak for you?

tiberiuzuld added a commit to tiberiuzuld/material that referenced this issue Mar 3, 2018
When destroying md-tooltip the callback `onDomAdded` remained attached and was causing a memory leak.

Added 4 lines with setting all 4 callbacks to null in `MdPanelRef.prototype.destroy`.

Fixes angular#11133
@fsmeier
Copy link
Contributor

fsmeier commented Mar 4, 2018

I will test it on monday.

@pbininda
Copy link
Author

pbininda commented Mar 6, 2018

I manually applied the change from #11145 to my local installation and can confirm, that it fixes the leak for me.
👍

@Splaktar Splaktar added the severity: regression This issue is related to a regression label Mar 10, 2018
@Splaktar Splaktar changed the title md-tooltip: memory leak tooltip: memory leak Apr 26, 2018
@Splaktar Splaktar changed the title tooltip: memory leak tooltip: memory leak on destroy Apr 26, 2018
chmelevskij pushed a commit to chmelevskij/material that referenced this issue Jun 19, 2018
When destroying md-tooltip the callback `onDomAdded` remained attached and was causing a memory leak.

Added 4 lines with setting all 4 callbacks to null in `MdPanelRef.prototype.destroy`.

Fixes angular#11133
Splaktar pushed a commit that referenced this issue Jul 31, 2018
When destroying md-tooltip the callback `onDomAdded` remained attached and was causing a memory leak.

Added 4 lines with setting all 4 callbacks to null in `MdPanelRef.prototype.destroy`.

Fixes #11133
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
has: Pull Request A PR has been created to address this issue P1: urgent Urgent issues that should be addressed in the next minor or patch release. resolution: fixed severity: memory leak Issues where a memory leak is triggered. severity: regression This issue is related to a regression type: bug
Projects
None yet
Development

No branches or pull requests

3 participants