内存泄漏 - 浏览器中的垃圾收集不收集组件
Memory Leak - Garbage Collection in Browser Not Collecting Components
Angular 组件内存泄漏 - Chrome 和 Microsoft Edge(可能是所有浏览器,尚未测试所有浏览器)
- 我已经在 Angular 11、12 和 13 的最新版本上尝试了下面的应用程序,我已经能够让它们全部泄漏内存
- 浏览器不会在 Angular 组件被销毁时对其进行垃圾回收
- 组件的浅尺寸(来自内存检查器)看起来很小,但保留的尺寸(来自内存检查器)很大
- DOM 节点保存在内存中,造成显着的内存泄漏
- 我已经能够让这个程序在 Chrome 和 Edge 中引起内存泄漏(还没有尝试过任何其他程序)
- 它发生在 angular
的开发和生产模式中
- 它可能与 css 属性、显示:flex
有关
- 我无法在 stackblitz 上重现泄漏。我只能让它在机器上工作 运行在本地安装应用程序
有时不会泄漏内存,有时会。这就是使它成为我一段时间以来遇到的最令人头疼的问题之一的原因。
如果你第一次运行下面的程序,请不要因为“我无法复制,所以它一定是假的”而立即注销而且它不会泄漏内存。请先尝试几次。我发誓它最终会在启动时进入一个状态(因为这个错误已经困扰我一段时间了),导致它泄漏内存。
我已经将它缩小到这个简单的应用程序,因为这个内存泄漏一直困扰着我一直在处理的一个更大的生产应用程序。
以下是可用于测试的简化应用程序。
https://github.com/kevinpbaker/angular-memory-killer
package.json
{
"name": "memory-killer",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --port 4201",
"build": "ng build --prod --aot --buildOptimizer --commonChunk --vendorChunk --optimization --progress",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~11.2.14",
"@angular/cdk": "^11.2.13",
"@angular/common": "~11.2.14",
"@angular/compiler": "~11.2.14",
"@angular/core": "~11.2.14",
"@angular/forms": "~11.2.14",
"@angular/material": "^11.2.13",
"@angular/platform-browser": "~11.2.14",
"@angular/platform-browser-dynamic": "~11.2.14",
"@angular/router": "~11.2.14",
"rxjs": "~6.5.4",
"rxjs-compat": "^6.5.5",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1102.18",
"@angular/cli": "~11.2.18",
"@angular/compiler-cli": "~11.2.14",
"@angular/language-service": "~11.2.14",
"@types/jasmine": "~3.6.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~4.0.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.3.13",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.7.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "4.0.8"
}
}
app.module.ts
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {AppComponent, MemoryKiller2Component, MemoryKillerComponent} from './app.component';
@NgModule({
declarations: [
AppComponent,
MemoryKillerComponent,
MemoryKiller2Component
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Memory Killer</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
app.component.ts
import {Component} from '@angular/core';
import {timer} from 'rxjs';
import {map} from 'rxjs/operators';
@Component({
selector: 'app-root',
template: '<app-memory-killer *ngIf="killCycle$|async"></app-memory-killer>',
})
export class AppComponent {
// change 1000 to 1 if you want to speed up the memory killer
killCycle$ = timer(0, 1000).pipe(map(k => k % 2 !== 0));
}
@Component({
selector: 'app-memory-killer',
template: `
<div class="memory-killer">
<app-memory-killer2></app-memory-killer2>
<app-memory-killer2></app-memory-killer2>
<app-memory-killer2></app-memory-killer2>
</div>
`,
styles: [`.memory-killer { display: flex; }`]
})
export class MemoryKillerComponent { }
@Component({
selector: 'app-memory-killer2',
template: `
<div class="memory-killer2">
<div>Killing your memory</div>
<div>Killing your memory</div>
<div>Killing your memory</div>
</div>
`,
styles: [`.memory-killer2 { display: flex; }`],
})
export class MemoryKiller2Component { }
Microsoft Edge Detached Elements Viewer and Memory Inspector - Version 97.0.1072.55
Chrome Memory Inspector - Version 97.0.4692.99
Visual of DOM Nodes Climbing - You can spam manual garbage collection and it won't collect the components
任何关于这是 angular 错误、浏览器错误的任何输入,或者如果你能指出合适的人可以帮助我解决这个问题,我们将不胜感激。
此问题是浏览器错误。它已由 chromium 开发团队修复。请参阅“原始 Angular 期 post”中的最后 post“
”
原始 Angular 问题 post:https://github.com/angular/angular/issues/45080
指定修复的 Chromium 错误:https://bugs.chromium.org/p/chromium/issues/detail?id=1308845
此错误已在 Chrome 版本 M102 中修复:See Chromium Bug Report.
The leak happens because LayoutInline gets added to TextAutoresizer::fingerprints_
but is never removed.
LayoutBlocks gets removed because it calls TextAutoresizer::Destroy()
from WillBeDestroyed()
.
The Oilpanize CL just made the leak bigger because now we retain LayoutObjects pointed by TextAutoresizer::fingerprints_
.
The leak detector could not catch this leak because TextAutoresizer is owned by the document so the leak will go away on navigation.
The following revision refers to this bug:
https://chromium.googlesource.com/chromium/src/+/3b3305a8d8566a9ba5ef6ccf9d363e6497f06356
commit 3b3305a8d8566a9ba5ef6ccf9d363e6497f06356
Author: Keishi Hattori keishi@chromium.org
Date: Mon Apr 04 23:34:17 2022
LayoutInline should be removed from TextAutosizer::FingerprintMapper
LayoutInline was not removed from TextAutosizer::FingerprintMapper::fingerprints_
causing a memory leak.
This CL removed LayoutInline from TextAutosizer in WillBeDestroyed()
.
Angular 组件内存泄漏 - Chrome 和 Microsoft Edge(可能是所有浏览器,尚未测试所有浏览器)
- 我已经在 Angular 11、12 和 13 的最新版本上尝试了下面的应用程序,我已经能够让它们全部泄漏内存
- 浏览器不会在 Angular 组件被销毁时对其进行垃圾回收
- 组件的浅尺寸(来自内存检查器)看起来很小,但保留的尺寸(来自内存检查器)很大
- DOM 节点保存在内存中,造成显着的内存泄漏
- 我已经能够让这个程序在 Chrome 和 Edge 中引起内存泄漏(还没有尝试过任何其他程序)
- 它发生在 angular 的开发和生产模式中
- 它可能与 css 属性、显示:flex 有关
- 我无法在 stackblitz 上重现泄漏。我只能让它在机器上工作 运行在本地安装应用程序
有时不会泄漏内存,有时会。这就是使它成为我一段时间以来遇到的最令人头疼的问题之一的原因。
如果你第一次运行下面的程序,请不要因为“我无法复制,所以它一定是假的”而立即注销而且它不会泄漏内存。请先尝试几次。我发誓它最终会在启动时进入一个状态(因为这个错误已经困扰我一段时间了),导致它泄漏内存。
我已经将它缩小到这个简单的应用程序,因为这个内存泄漏一直困扰着我一直在处理的一个更大的生产应用程序。
以下是可用于测试的简化应用程序。
https://github.com/kevinpbaker/angular-memory-killer
package.json
{
"name": "memory-killer",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --port 4201",
"build": "ng build --prod --aot --buildOptimizer --commonChunk --vendorChunk --optimization --progress",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~11.2.14",
"@angular/cdk": "^11.2.13",
"@angular/common": "~11.2.14",
"@angular/compiler": "~11.2.14",
"@angular/core": "~11.2.14",
"@angular/forms": "~11.2.14",
"@angular/material": "^11.2.13",
"@angular/platform-browser": "~11.2.14",
"@angular/platform-browser-dynamic": "~11.2.14",
"@angular/router": "~11.2.14",
"rxjs": "~6.5.4",
"rxjs-compat": "^6.5.5",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1102.18",
"@angular/cli": "~11.2.18",
"@angular/compiler-cli": "~11.2.14",
"@angular/language-service": "~11.2.14",
"@types/jasmine": "~3.6.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~4.0.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.3.13",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.7.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "4.0.8"
}
}
app.module.ts
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {AppComponent, MemoryKiller2Component, MemoryKillerComponent} from './app.component';
@NgModule({
declarations: [
AppComponent,
MemoryKillerComponent,
MemoryKiller2Component
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Memory Killer</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
app.component.ts
import {Component} from '@angular/core';
import {timer} from 'rxjs';
import {map} from 'rxjs/operators';
@Component({
selector: 'app-root',
template: '<app-memory-killer *ngIf="killCycle$|async"></app-memory-killer>',
})
export class AppComponent {
// change 1000 to 1 if you want to speed up the memory killer
killCycle$ = timer(0, 1000).pipe(map(k => k % 2 !== 0));
}
@Component({
selector: 'app-memory-killer',
template: `
<div class="memory-killer">
<app-memory-killer2></app-memory-killer2>
<app-memory-killer2></app-memory-killer2>
<app-memory-killer2></app-memory-killer2>
</div>
`,
styles: [`.memory-killer { display: flex; }`]
})
export class MemoryKillerComponent { }
@Component({
selector: 'app-memory-killer2',
template: `
<div class="memory-killer2">
<div>Killing your memory</div>
<div>Killing your memory</div>
<div>Killing your memory</div>
</div>
`,
styles: [`.memory-killer2 { display: flex; }`],
})
export class MemoryKiller2Component { }
Microsoft Edge Detached Elements Viewer and Memory Inspector - Version 97.0.1072.55
Chrome Memory Inspector - Version 97.0.4692.99
Visual of DOM Nodes Climbing - You can spam manual garbage collection and it won't collect the components
任何关于这是 angular 错误、浏览器错误的任何输入,或者如果你能指出合适的人可以帮助我解决这个问题,我们将不胜感激。
此问题是浏览器错误。它已由 chromium 开发团队修复。请参阅“原始 Angular 期 post”中的最后 post“
”原始 Angular 问题 post:https://github.com/angular/angular/issues/45080
指定修复的 Chromium 错误:https://bugs.chromium.org/p/chromium/issues/detail?id=1308845
此错误已在 Chrome 版本 M102 中修复:See Chromium Bug Report.
The leak happens because LayoutInline gets added to
TextAutoresizer::fingerprints_
but is never removed. LayoutBlocks gets removed because it callsTextAutoresizer::Destroy()
fromWillBeDestroyed()
. The Oilpanize CL just made the leak bigger because now we retain LayoutObjects pointed byTextAutoresizer::fingerprints_
.The leak detector could not catch this leak because TextAutoresizer is owned by the document so the leak will go away on navigation.
The following revision refers to this bug: https://chromium.googlesource.com/chromium/src/+/3b3305a8d8566a9ba5ef6ccf9d363e6497f06356
commit 3b3305a8d8566a9ba5ef6ccf9d363e6497f06356 Author: Keishi Hattori keishi@chromium.org Date: Mon Apr 04 23:34:17 2022
LayoutInline should be removed from
TextAutosizer::FingerprintMapper
LayoutInline was not removed from
TextAutosizer::FingerprintMapper::fingerprints_
causing a memory leak.This CL removed LayoutInline from TextAutosizer in
WillBeDestroyed()
.