fix(positioning): prevent memory leak in positioning service #6724
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
While investigating performance issues on a page with a lot of tooltips, I stumbled on a memory leak in ngx-bootstrap.
Reproducer
Small reproducer creating a bunch of tooltip:
https://stackblitz.com/edit/stackblitz-starters-w3my1g3z?file=src%2Fmain.ts
steps:
stackblitz[something]--4200--[something].webcontainer.io
). Otherwise you will only see the memory usage for the stackblitz editor itself. Alternatively you can open the preview in another tab.about:memory
in firefox)result:
Explanation
PositioningService
never unsubscribes fromtriggerEvent$
. It would be mostly harmless if there was only one instance of the service but we get one instance ofPositioningService
per tooltip because it is explicitly in the directiveproviders
.ngx-bootstrap/src/tooltip/tooltip.directive.ts
Lines 30 to 34 in 17a0f5d
I settled on simply adding
takeUntilDestroyed
in the service since it is less likely to have some unforeseen impact in other components compared to force the service to be a singleton.After the change and using the same scenario the memory stays roughly equivalent between the two snapshots.
note:
I'm not sure if having several instances is intentional or not: since we have
{providedIn: 'root'}
I initially thought that it was a mistake and it should be a singleton, but the service also exposesetOptions
which makes it mandatory to have several instances if we want to have different options per component (I think #5706 is an attempt to fix that).PR Checklist
Before creating new PR, please take a look at checklist below to make sure that you've done everything that needs to be done before we can merge it.