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
__ngContext__ magnifies native Chrome memory leaks #41047
Comments
My team is working around this by patching attachPatchData to: export function attachPatchData(target: any, data: LView|LContext) {
if (ngDevMode || !(target instanceof EventTarget)) {
target[MONKEY_PATCH_KEY_NAME] = data;
}
} With the change, our stress tests leak 50mb instead of 325mb. The question is, are production Angular apps reliant on DOM-nodes getting patched with |
@mhevery is |
Any updates on this? |
It is necessary for some APIs, so it is not dev only. My understanding is that this is an issue with Chrome and there is not much we can do about this. |
Closing this ticket based on Misko's reply above. Detached DOM elements are retained in memory due to Chrome bug and these elements refer to some internal Angular data structures that become non-garbage-collected because of that. Once the issue is resolved in Chrome, the memory leak should be gone. |
This is quite an issue for any larger projects migrating to Ivy, since it is not only the chrome issue. Any existing leaks (detached DOM nodes) will grow substantially and if you are deploying in a shared environment with limited RAM, this has quite an impact. We should fix any leaks we have but the chrome issue we cannot fix and it looks to me like the monkey patch also makes the process of finding the leaks much more difficult, since everything is retain by ngContext? If we are right in that the monkey patch increases any existing leaks and that it prevents us from finding the real retainers, closing this issue means any larger projects should stick with view engine until they have fixed all their leaks (the chrome issue not even possible to fix). We have already migrated and are left with patching angular core, both to reduce the leaks in size and to be able to get the real retainers. Are we missing something here? I think you currently have this issue on angular.io. Not sure if it is only because of the chrome issue or if you have other leaks as well. |
Besides that I agree with @kallebjork I'm also curious about what APIs need this in production and why. |
@mhevery Does this also apply to patching native elements specifically? From our perspective, this issue would put 100.000 healthcare workers on ViewEngine indefinitely. That's a huge blow, because Ivy is amazing and because we'll be stranded with an old Angular version. The background here is that the hospital environments usually have 100-200 MB of RAM. And if the framework magnifies leaks by a factor thousand, those environments will go down. I get that the reproduction above uses a Chrome issue, but the role of I'm happy to rephrase the issue to "ngContext magnifies memory leaks" if you think that is a better angle here. |
This may related to the issue here #36011, it seems to be an issue from chrome https://bugs.chromium.org/p/v8/issues/detail?id=10297#c5, and maybe we can provide a API to clear the |
We discussed this some more and think we might be able to use |
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 angular#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 an array where their unique ID corresponds to an index. Using an array should guarantee fast creation, destruction and lookups. We don't need to worry about leaks within that array, because `LView`s are an internal data structure and we have complete control over when they are created and destroyed. Fixes angular#41047.
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 angular#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 an array where their unique ID corresponds to an index. Using an array should guarantee fast creation, destruction and lookups. We don't need to worry about leaks within that array, because `LView`s are an internal data structure and we have complete control over when they are created and destroyed. Fixes angular#41047.
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 angular#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 an array where their unique ID corresponds to an index. Using an array should guarantee fast creation, destruction and lookups. We don't need to worry about leaks within that array, because `LView`s are an internal data structure and we have complete control over when they are created and destroyed. Fixes angular#41047.
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 angular#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 an array where their unique ID corresponds to an index. Using an array should guarantee fast creation, destruction and lookups. We don't need to worry about leaks within that array, because `LView`s are an internal data structure and we have complete control over when they are created and destroyed. Fixes angular#41047.
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 angular#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 an array where their unique ID corresponds to an index. Using an array should guarantee fast creation, destruction and lookups. We don't need to worry about leaks within that array, because `LView`s are an internal data structure and we have complete control over when they are created and destroyed. Fixes angular#41047.
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 angular#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 an array where their unique ID corresponds to an index. Using an array should guarantee fast creation, destruction and lookups. We don't need to worry about leaks within that array, because `LView`s are an internal data structure and we have complete control over when they are created and destroyed. Fixes angular#41047.
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 angular#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 an array where their unique ID corresponds to an index. Using an array should guarantee fast creation, destruction and lookups. We don't need to worry about leaks within that array, because `LView`s are an internal data structure and we have complete control over when they are created and destroyed. Fixes angular#41047.
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 angular#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 an array where their unique ID corresponds to an index. Using an array should guarantee fast creation, destruction and lookups. We don't need to worry about leaks within that array, because `LView`s are an internal data structure and we have complete control over when they are created and destroyed. Fixes angular#41047.
@denspi see the comment above, we ended up reverting the fix due to concerns around backwards compatibility. |
Thanks, just wanted to make sure I havent missed something. |
@KimAlexander we ended up reverting #41358 due to some issues. See the discussion above. |
Any ETA on a fix for this? |
@codebreach I just created a directive which in ngOnDestroy gets ngContext, finds AnimationRendererFactory.engine._transitionEngine and clears statesByElement and playersByElement arrays. Works fine by now. |
For anybody looking for an ugly workaround, I've yet to see any issues with this patch of import { process as runNgcc } from '@angular/compiler-cli/ngcc';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
const ANGULAR_CORE_PATH = '@angular/core';
const ANGULAR_CORE_NGCC_PATH = `${ANGULAR_CORE_PATH}/__ivy_ngcc__/fesm2015/core.js`;
const REPLACE_AT = `
function attachPatchData(target, data) {
ngDevMode && assertDefined(target, 'Target expected');
target[MONKEY_PATCH_KEY_NAME] = data;
}
`.trim();
const REPLACE_WITH = `
function attachPatchData(target, data) {
ngDevMode && assertDefined(target, 'Target expected');
if (ngDevMode || !(target instanceof EventTarget)) {
target[MONKEY_PATCH_KEY_NAME] = data;
}
}
`.trim();
// https://github.com/angular/angular/issues/41047
export function patchAngularToRemoveNgContextFromDom(nodeModulesPath: string): void {
const patchFilePath = join(nodeModulesPath, ANGULAR_CORE_NGCC_PATH);
if (!existsSync(patchFilePath)) {
runNgcc({
basePath: nodeModulesPath,
targetEntryPointPath: join(nodeModulesPath, ANGULAR_CORE_PATH),
createNewEntryPointFormats: true,
propertiesToConsider: ['fesm2015'],
});
if (!existsSync(patchFilePath)) {
throw new Error(`Tried to patch @angular/core but could not find ${patchFilePath}.`);
}
}
const fileContent = readFileSync(patchFilePath, 'utf8');
const replacedFileContent = fileContent.replace(REPLACE_AT, REPLACE_WITH);
if (!replacedFileContent.includes(REPLACE_WITH)) {
throw new Error(`Could not patch memory leak in @angular/core.`);
}
if (fileContent.length === replacedFileContent.length) {
return;
}
writeFileSync(patchFilePath, replacedFileContent);
} |
You are a life saver! With a similar fix our app is now smooth and performant on even the slower iOs Air2s ( ours is a hybrid ionic Ng app ). Basically we have 100s of cards that can be paginated back and forth and the dom nodes and memory were climbing up. We added this "hack" and the UI is now much better than ever.
and our patch , in npm post-install is to call this after the
The downside is we have to keep track when we update Angular |
These changes combine angular#41358 and angular#41894. 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 angular#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 angular#41047.
These changes combine angular#41358 and angular#41894. 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 angular#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 angular#41047.
These changes combine angular#41358 and angular#41894. 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 angular#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 angular#41047.
These changes combine angular#41358 and angular#41894. 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 angular#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 angular#41047.
These changes combine angular#41358 and angular#41894. 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 angular#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 angular#41047.
These changes combine angular#41358 and angular#41894. 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 angular#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 angular#41047.
These changes combine angular#41358 and angular#41894. 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 angular#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 angular#41047.
These changes combine angular#41358 and angular#41894. 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 angular#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 angular#41047.
We are using micro front-end architecture with ngx-build-plus and Angular v10.2. We tried the above mentioned approach & we got the benefit as the reference of WebComponent class is not leaking any memory. However, all of the DOM Nodes of this web component are getting detached( Detached HTMLElement/Div Element, etc) when the component is destroyed. Can someone help with this? Thanks! |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
These changes combine angular#41358 and angular#41894. 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 angular#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 angular#41047. PR Close angular#45051
🐞 bug report
Affected Package
Is this a regression?
No.
Description
Small memory leaks become large leaks when Angular monkey patches
__ngContext__
onto dom.For example, take this issue. After typing into textareas, the issue leads to detached HTMLTextAreaElements because of the native undo stack.
It is a small leak that has nothing to do with Angular. It is only a few KBs:
But, with Angular components in the mix, those KBs become MBs:
It looks like
__ngContext__
, retains almost everything, even though the textarea is a vanilla element:And if we remove this call from
@angular/core
:🔬 Minimal Reproduction
https://github.com/blidblid/ng-context-memory-leak
The issue is also present on material.angular.io. Typing in a textarea and navigating the sidenav will cause large leaks.
🌍 Your Environment
Angular Version:
Anything else relevant?
The text was updated successfully, but these errors were encountered: