Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leak #304

Open
SergiyCh opened this issue Nov 3, 2023 · 6 comments
Open

Memory leak #304

SergiyCh opened this issue Nov 3, 2023 · 6 comments
Labels
bug Something isn't working

Comments

@SergiyCh
Copy link

SergiyCh commented Nov 3, 2023

Please answer the following questions for yourself before submitting an issue.
YOU MAY DELETE THE PREREQUISITES SECTION.

  • [ x] I am running the latest version
  • [ x] I checked the Readme and found no answer
  • [ x] I checked to make sure that this issue has not already been filed
  • [ x] This is related to angular-google-charts and not to Google Charts directly (if it's a Google Charts issue, their forum may help)

Description

When a component with google-chart is destroyed the memory related to Google chart remains.

To reproduce

Steps to reproduce the behaviour:

  1. Create a component with google-chart and provide some data so a chart is drawn.
  2. Create/destroy the component several times, e.g. using *ngFor with updated source.
  3. Open chrome devtools, create two memory snapshots before and after component destroys, and check objects.
  4. See a lot of google objects. They are only increasing.

Expected behaviour

Angular google chart component must clear the chart on destroy using clearChart() method of google charts.

Workaround

Call chart.clearChart() on ngDestroy() for parent component. chart is obtained using "ready" callback.

See the same workaround for Vue:
devstark-com/vue-google-charts#25

@SergiyCh SergiyCh added the bug Something isn't working label Nov 3, 2023
@kussmaul
Copy link
Contributor

kussmaul commented Sep 1, 2024

As noted in #230, I don't see clearChart() in the angular-google-chart code - it seems to have been added in #70, but maybe refactored away since then. Here is my (kludgy) code to call the underlying clearCharts:

/** Chart component(s). */  @ViewChildren (GoogleChartComponent)  charts    ! : QueryList<GoogleChartComponent>;

  ngOnDestroy() {
    this.charts.forEach((c) => (c.chart as unknown as { clearChart : () => void })?.clearChart());
  }

@FERNman
Copy link
Owner

FERNman commented Sep 2, 2024

@kussmaul A while ago, I refactored the library to use a ChartWrapper (always) to instantiate the charts. It has a getChart method that returns the interface google.visualization.ChartBase, which doesn't contain the clearChart method. One needs to check for the existence of clearChart on the chart property since some charts might not support it, and only then call it.

However, I'm quite convinced that calling clearChart to resolve the memory leak is not a fix but rather a workaround. In the Google Charts community, they also said that browsers should be able to clean up the chart if no references are around anymore. I'd guess that maybe looking into the event handlers we're registering in our components could yield some results, even though one would probably have to dive a bit deeper into this library and Google Charts itself.

@kussmaul
Copy link
Contributor

kussmaul commented Sep 9, 2024

+1 to fixes rather than workarounds. :-)

I see components (chart-editor, chart-wrapper, control-wrapper, dashboard) where ngOnit() calls subscribe() on loadChartPackages() without a matching unsubscribe(). Would this help?

@FERNman
Copy link
Owner

FERNman commented Sep 9, 2024

It's probably worth looking into if you find the time :) Also, a reproduction of this issue (Stackblitz or Codespaces) would be great!

I'm not entirely sure about subjects within components anymore. If that can cause issues, it might be the wrapperReadySubject in the GoogleChartComponent that's causing the issue.

@kussmaul
Copy link
Contributor

Here is some more data, which might be useful, although it is definitely not a minimal reproduction. :-)

In Chrome DevTools, I used the Performance tab to record heap, nodes, listeners, etc. as I use my app to create ~30 angular-google-chart tables, wait ~5 seconds, delete them, wait ~5 seconds, create them again, etc.

Here is the timeline without clearChart() in ngOnDestroy():
2024-09-10 clearChart off

Here is the timeline with clearChart() in ngOnDestroy():

2024-09-10 clearChart on

It looks like clearChart() does reduce the heap, but nodes and listeners continue to increase. This might be my code, or angular-google-charts, or GoogleCharts, I suppose. :-)

@kussmaul
Copy link
Contributor

I also looked at listeners:
https://github.com/search?q=repo%3AFERNman%2Fangular-google-charts%20Listener&type=code

I see components (chart-wrapper, control-wrapper, dashboard, google-chart) where ngOnInit() (via private helper methods) calls google.visualization.events.addListener() 2-3 times without a matching removeListener() or removeAllListeners(). However, removeAllListeners() is called from ngOnInit() before addListener().

In contrast, chart-editor-ref calls addOneTimeListener() (twice) with a callback that includes 'removeAllListeners()`.

@FERNman, do you recall why removeAllListeners() is called from ngOnInit()?

Should these 4 classes call removeAllListeners() in ngOnDestroy()?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants