visjs/ngx-vis

Memory leak in Network

wajda opened this issue · 7 comments

wajda commented

Apparently calling Network.destroy() from ngOnDestroy() is not enough for Vis to do a proper cleanup in Angular. It leaves a bunch of detached nodes retained by different kind of listeners.
You can see it even on the demo page - https://hypery2k.github.io/

Steps to reproduce:

  1. From the demo home page open the Network example (by clicking on the "Example" button)
  2. Start memory profiling / take an initial memory snapshot in the Chrome Dev Tools
  3. Click "Home" button to return on the Demo home page.
  4. Click the "Example" button to re-enter the Network demo page.
  5. Take another memory snapshot.
  6. Repeat several times.
  7. Stop memory profiling.
  8. In the captured memory snapshots search for "detached" and notice growing amount of detached objects from one snapshot to another.
  9. In the recorded memory timeline notice the growing JS heap, number of nodes and listeners.

Screenshot 2019-04-26 at 12 34 21

I have tried the simplest scenario in my blank project with only one directive that creates and destroys an empty Network object (with zero nodes in it) when the component is created and destroyed respectively. And it already leaked.

I have also tried wrapping the network creation code with the Zone.runOutsideAngular() hoping that it would decrease the number of retention points for vis nodes and listeners, but it didn't.

Apparently this leakage only happens when Vis is used together with Angular.

Do you have any ideas where the root cause can lie and what could be a possible workaround?

Thank you.

actually I cannot reproduce your issues. My numbers are not increasing

I had a similar memory leak issue caused by vis creating listeners for each subscribe I did on EventEmitters such as click and afterDrawing. The network.destroy called at ngOnDestroy does not unsubscribe from these listeners when the component is destroyed and created multiple times.

Workaround:

Create a new observable
private ngUnsubscribe: Subject<void> = new Subject();

When subscribing to an event, use takeUntil
this.visNetworkService.click.takeUntil(this.ngUnsubscribe).subscribe((eventData: any[]) => { });

Finally do ngUnsubscribe.next in ngOnDestroy to manually unsubscribe from these events when the component is destroyed.

public ngOnDestroy(): void {
    this.ngUnsubscribe.next();
    this.visNetworkService.destroy(this.visNetwork);
 }

I can use method with takeUntil...

What is proper way to solve this issue with doubling of listeners?

did you have a code snippet?

relates to #399

you give ngx-vis@3.1.0-build1589705139 a try