Sitelet https://github.com/angular/components/issues/31717
Skip to content

bug(CdkTreeNode): Potential memory leak caused by missing takeUntil and _tree._unregisterNode in ngOnDestroy #31717

Description

@koleckoSG

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

No response

Description

https://github.com/angular/components/blob/652dd334ff22568f6e1d02ef2e7741be10cdc867/src/cdk/tree/tree.ts#L1396C3-L1407C4

Missing takeUntil in the CdkTreeNode registers subscriber and leaves it dangling after destruction.

This is not the only place preventing GC to collect TreeNode, other problem is _nodes Map used for BehaviourSubject value mapping Data to TreeNode. This map keeps old values.

I see the takeUntil was added in patch 20.0.5, I don't know whether it's safe to patch it in version 19.

I do not see CdkTreeNode.ngOnDestroy containing unregisterNode and I do not know why is that, while ngOnInit contains registerNode. Don't know whether it's mistake or intention to leave it registered even on destroy.

Reproduction

StackBlitz link (needed modifications in steps to reproduce): https://stackblitz.com/run?file=src%2Fexample%2Ftree-nested-child-accessor-overview-example.ts
Steps to reproduce:

  1. open up example controller from the provided stackBlitz link
  2. set dataSource to new BehaviorSubject<FoodNode[]>([]);
  3. add the constructor to consecutively change dataSource nodes
  constructor(private readonly cdr: ChangeDetectorRef) {
      this.rs();
  }

  private rs(): void {
      if (this.i === 5) { 
          return
      }
      setTimeout(() => {
          this.dataSource.next([{
              name: 'Fruite ' + ++this.i,
              children: [{ name: 'Apple' }, { name: 'Banana' }, { name: 'Fruit loops' }],
          }, {
              name: 'Salame ' + this.i,
              children: [{ name: 'Apple' }, { name: 'Banana' }, { name: 'Fruit loops' }],
          }])
          this.rs();
    },300);
  }
  1. Debug detached nodes in chrome
  2. When fixing this issue alone _nodes value map in Tree has to be also cleaned
// Suggested fix for the point #5 is to:

export class CdkTreeNode...

...ngOnDestroy(): void {
....
this._tree.unregisterNode(this) // to match register node in ngOnInit
...

Expected Behavior

TakeUntil should be implemented to prevent dangling references and enable cleanup by GC.

Actual Behavior

Nodes kept detached unable to be cleaned up by GC.

Environment

  • Angular: 19.2.19
  • CDK/Material: 19.2.19
  • Browser(s): any
  • Operating System (e.g. Windows, macOS, Ubuntu): any

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgentarea: cdk/tree

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions