perf(core): avoid storing LView in __ngContext__ (#41358)
Currently we save a reference to an `LView` on most DOM nodes created by Angular either by saving the `LView` directly in the `__ngContext__` or by saving the `LContext` which has a reference to the `LView`. This can be a problem if the DOM node is retained in memory, because the `LView` has references to all of the child nodes of the view, as well as other internal data structures. Previously we tried to resolve the issue by clearing the `__ngContext__` when a node is removed (see https://github.com/angular/angular/pull/36011), but we decided not to proceeed, because it can slow down destruction due to a megamorphic write. These changes aim to address the issue while reducing the performance impact by assigning a unique ID when an `LView` is created and adding it to `__ngContext__`. All active views are tracked in a map where their unique ID is used as the key. We don't need to worry about leaks within that map, because `LView`s are an internal data structure and we have complete control over when they are created and destroyed. Fixes #41047. PR Close #41358
This commit is contained in:
committed by
Jessica Janiuk
parent
6d9e3400e1
commit
18b33e79d3
@@ -262,9 +262,10 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
const context = getLContext(this.nativeNode);
|
||||
if (context !== null) {
|
||||
const lView = context.lView;
|
||||
const context = getLContext(this.nativeNode)!;
|
||||
const lView = context ? context.lView : null;
|
||||
|
||||
if (lView !== null) {
|
||||
const tData = lView[TVIEW].data;
|
||||
const tNode = tData[context.nodeIndex] as TNode;
|
||||
return tNode.value!;
|
||||
@@ -286,12 +287,13 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
||||
* - attribute bindings (e.g. `[attr.role]="menu"`)
|
||||
*/
|
||||
get properties(): {[key: string]: any;} {
|
||||
const context = getLContext(this.nativeNode);
|
||||
if (context === null) {
|
||||
const context = getLContext(this.nativeNode)!;
|
||||
const lView = context ? context.lView : null;
|
||||
|
||||
if (lView === null) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const lView = context.lView;
|
||||
const tData = lView[TVIEW].data;
|
||||
const tNode = tData[context.nodeIndex] as TNode;
|
||||
|
||||
@@ -312,12 +314,13 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
||||
return attributes;
|
||||
}
|
||||
|
||||
const context = getLContext(element);
|
||||
if (context === null) {
|
||||
const context = getLContext(element)!;
|
||||
const lView = context ? context.lView : null;
|
||||
|
||||
if (lView === null) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const lView = context.lView;
|
||||
const tNodeAttrs = (lView[TVIEW].data[context.nodeIndex] as TNode).attrs;
|
||||
const lowercaseTNodeAttrs: string[] = [];
|
||||
|
||||
@@ -502,11 +505,12 @@ function _queryAllR3(
|
||||
function _queryAllR3(
|
||||
parentElement: DebugElement, predicate: Predicate<DebugElement>|Predicate<DebugNode>,
|
||||
matches: DebugElement[]|DebugNode[], elementsOnly: boolean) {
|
||||
const context = getLContext(parentElement.nativeNode);
|
||||
if (context !== null) {
|
||||
const parentTNode = context.lView[TVIEW].data[context.nodeIndex] as TNode;
|
||||
const context = getLContext(parentElement.nativeNode)!;
|
||||
const lView = context ? context.lView : null;
|
||||
if (lView !== null) {
|
||||
const parentTNode = lView[TVIEW].data[context.nodeIndex] as TNode;
|
||||
_queryNodeChildrenR3(
|
||||
parentTNode, context.lView, predicate, matches, elementsOnly, parentElement.nativeNode);
|
||||
parentTNode, lView, predicate, matches, elementsOnly, parentElement.nativeNode);
|
||||
} else {
|
||||
// If the context is null, then `parentElement` was either created with Renderer2 or native DOM
|
||||
// APIs.
|
||||
|
||||
Reference in New Issue
Block a user