Javascript 减少Angular应用程序的内存利用率
我创建了一个包含一个表单和一个表(Priming DataTable)的数据库 为了在初始和最终读取时获得内存利用率的显著差异,我执行了多次表单Post调用(300次),并将表导航到第20页,每页1000行,还对表中的所有列进行了5次排序,并拍摄了具有初始和最终状态的堆快照(在private/incognito选项卡中禁用所有附加组件,并且在dev env中运行angular cli应用程序) 在chrome中,堆内存大小从55MB增加到146MB(增加91MB) 在Chrome中,堆内存大小从23.16 MB增加到137.1 MB(增加113.95 MB) 当我的组件销毁时,我将取消订阅所有订阅(这将没有任何影响,因为这是一个单一组件),并且我已将changeDetectionStrategy设置为onPush app.component.html:Javascript 减少Angular应用程序的内存利用率,javascript,angular,performance,memory-leaks,angular5,Javascript,Angular,Performance,Memory Leaks,Angular5,我创建了一个包含一个表单和一个表(Priming DataTable)的数据库 为了在初始和最终读取时获得内存利用率的显著差异,我执行了多次表单Post调用(300次),并将表导航到第20页,每页1000行,还对表中的所有列进行了5次排序,并拍摄了具有初始和最终状态的堆快照(在private/incognito选项卡中禁用所有附加组件,并且在dev env中运行angular cli应用程序) 在chrome中,堆内存大小从55MB增加到146MB(增加91MB) 在Chrome中,堆内存大小
<div [ngBusy]="{busy: busy, message: 'Loading data, this may take few minutes!'}"></div>
<div style="width:50%">
<form [formGroup]="taskForm" (ngSubmit)="addTask(taskForm.value)">
<div class="width:100%;float:left; clear:both;">
<div style="width:20%;float:left">
<input type="text" [formControl]="index" class="form-control" placeholder="index" />
</div>
<div style="width:20%;float:left">
<input type="text" [formControl]="name" class="form-control" placeholder="name" />
</div>
<div style="width:20%;float:left">
<input type="text" [formControl]="userId" class="form-control" placeholder="userId" />
</div>
<div style="width:20%;float:left">
<input type="text" [formControl]="mobile" class="form-control" placeholder="mobile" />
</div>
<div style="width:20%;float:left">
<input type="date" [formControl]="taskDate" class="form-control" placeholder="taskDate" />
</div>
</div>
<div class="width:100%;float:left; clear:both;">
<button type="submit" class="btn btn-success">Add Task</button>{{taskPostCount}}
</div>
</form>
<code *ngIf="addTaskResponse">{{addTaskResponse | json}}</code>
</div>
<div style="text-align:center">
<p-dataTable [dataKey]="'_id'" *ngIf="isTableVisible()" [value]="table" expandableRows="true" (onFilter)="onColumnFilterChanged($event)"
(onSort)="onTableColumnSortChanged($event)" [lazy]="true">
<p-column field="index" header="Index" [filter]="true" [sortable]="true"></p-column>
<p-column field="name" header="Task Name" [filter]="true" [sortable]="true"></p-column>
<p-column field="mobile" header="Mobile" [filter]="true" [sortable]="true"></p-column>
<p-column field="taskDate" header="Task Date"></p-column>
<p-column field="createdAt" header="Date Created"></p-column>
<p-column field="updatedAt" header="Date Updated"></p-column>
</p-dataTable>
<p-paginator [rowsPerPageOptions]="[10,20,30,50,100,200,300,400,500,1000]" [first]="tableSearchConfig.firstRowIndex" [totalRecords]="tableSearchConfig.totalRecords"
*ngIf="isTableVisible()" [rows]="tableSearchConfig.rowsPerPage" (onPageChange)="onPageChanged($event)"></p-paginator>
</div>
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [AppService, TimeFromNow],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
// Properties declarations
private unsubscribe: Subject<boolean> = new Subject<boolean>();
constructor(private appService: AppService, private timeFromNow: TimeFromNow, private ref: ChangeDetectorRef, fb: FormBuilder) {
this.taskForm = fb.group({
'index': ['', Validators.compose([])],
'name': ['', Validators.compose([])],
'userId': ['', Validators.compose([])],
'mobile': ['', Validators.compose([])],
'taskDate': ['', Validators.compose([])],
});
this.index = this.taskForm.controls['index'];
this.name = this.taskForm.controls['name'];
this.userId = this.taskForm.controls['userId'];
this.mobile = this.taskForm.controls['mobile'];
this.taskDate = this.taskForm.controls['taskDate'];
this.setTableSearchConfig();
}
ngOnInit() {
this.addBulkTasksOnLoad();
}
ngOnDestroy() {
this.unsubscribe.next(true);
this.unsubscribe.complete();
this.unsubscribe.unsubscribe();
}
addBulkTasksOnLoad() {
this.busy = this.appService.addTaskOnLoad().subscribe((res: any) => {
this.loadTable();
}, (err: any) => {
});
}
addTask(taskForm: any) {
this.taskPostCount++;
this.appService.addTask(taskForm).takeUntil(this.unsubscribe).subscribe((res: any) => {
this.addTaskResponse = res;
},
err => {
this.addTaskResponse = err;
});
}
loadTable(paginateEvent?: PaginateEvent, sortEvent?: SortEvent, filterEvent?: FilterEvent) {
this.appService.getTable(this.tableSearchConfig).takeUntil(this.unsubscribe).subscribe((res: any) => {
for (const history of res.data) {
history.updatedAt = this.timeFromNow.transform(history.updatedAt);
}
this.table = res.data;
this.setTableSearchConfig(paginateEvent, sortEvent, filterEvent, this.tableSearchConfig.pageNumberToRequest, res.totalRecords);
this.ref.detectChanges();
});
}
.
.
.
.
}
添加任务{{taskPostCount}}
{{addTaskResponse | json}}
app.component.ts:
<div [ngBusy]="{busy: busy, message: 'Loading data, this may take few minutes!'}"></div>
<div style="width:50%">
<form [formGroup]="taskForm" (ngSubmit)="addTask(taskForm.value)">
<div class="width:100%;float:left; clear:both;">
<div style="width:20%;float:left">
<input type="text" [formControl]="index" class="form-control" placeholder="index" />
</div>
<div style="width:20%;float:left">
<input type="text" [formControl]="name" class="form-control" placeholder="name" />
</div>
<div style="width:20%;float:left">
<input type="text" [formControl]="userId" class="form-control" placeholder="userId" />
</div>
<div style="width:20%;float:left">
<input type="text" [formControl]="mobile" class="form-control" placeholder="mobile" />
</div>
<div style="width:20%;float:left">
<input type="date" [formControl]="taskDate" class="form-control" placeholder="taskDate" />
</div>
</div>
<div class="width:100%;float:left; clear:both;">
<button type="submit" class="btn btn-success">Add Task</button>{{taskPostCount}}
</div>
</form>
<code *ngIf="addTaskResponse">{{addTaskResponse | json}}</code>
</div>
<div style="text-align:center">
<p-dataTable [dataKey]="'_id'" *ngIf="isTableVisible()" [value]="table" expandableRows="true" (onFilter)="onColumnFilterChanged($event)"
(onSort)="onTableColumnSortChanged($event)" [lazy]="true">
<p-column field="index" header="Index" [filter]="true" [sortable]="true"></p-column>
<p-column field="name" header="Task Name" [filter]="true" [sortable]="true"></p-column>
<p-column field="mobile" header="Mobile" [filter]="true" [sortable]="true"></p-column>
<p-column field="taskDate" header="Task Date"></p-column>
<p-column field="createdAt" header="Date Created"></p-column>
<p-column field="updatedAt" header="Date Updated"></p-column>
</p-dataTable>
<p-paginator [rowsPerPageOptions]="[10,20,30,50,100,200,300,400,500,1000]" [first]="tableSearchConfig.firstRowIndex" [totalRecords]="tableSearchConfig.totalRecords"
*ngIf="isTableVisible()" [rows]="tableSearchConfig.rowsPerPage" (onPageChange)="onPageChanged($event)"></p-paginator>
</div>
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [AppService, TimeFromNow],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
// Properties declarations
private unsubscribe: Subject<boolean> = new Subject<boolean>();
constructor(private appService: AppService, private timeFromNow: TimeFromNow, private ref: ChangeDetectorRef, fb: FormBuilder) {
this.taskForm = fb.group({
'index': ['', Validators.compose([])],
'name': ['', Validators.compose([])],
'userId': ['', Validators.compose([])],
'mobile': ['', Validators.compose([])],
'taskDate': ['', Validators.compose([])],
});
this.index = this.taskForm.controls['index'];
this.name = this.taskForm.controls['name'];
this.userId = this.taskForm.controls['userId'];
this.mobile = this.taskForm.controls['mobile'];
this.taskDate = this.taskForm.controls['taskDate'];
this.setTableSearchConfig();
}
ngOnInit() {
this.addBulkTasksOnLoad();
}
ngOnDestroy() {
this.unsubscribe.next(true);
this.unsubscribe.complete();
this.unsubscribe.unsubscribe();
}
addBulkTasksOnLoad() {
this.busy = this.appService.addTaskOnLoad().subscribe((res: any) => {
this.loadTable();
}, (err: any) => {
});
}
addTask(taskForm: any) {
this.taskPostCount++;
this.appService.addTask(taskForm).takeUntil(this.unsubscribe).subscribe((res: any) => {
this.addTaskResponse = res;
},
err => {
this.addTaskResponse = err;
});
}
loadTable(paginateEvent?: PaginateEvent, sortEvent?: SortEvent, filterEvent?: FilterEvent) {
this.appService.getTable(this.tableSearchConfig).takeUntil(this.unsubscribe).subscribe((res: any) => {
for (const history of res.data) {
history.updatedAt = this.timeFromNow.transform(history.updatedAt);
}
this.table = res.data;
this.setTableSearchConfig(paginateEvent, sortEvent, filterEvent, this.tableSearchConfig.pageNumberToRequest, res.totalRecords);
this.ref.detectChanges();
});
}
.
.
.
.
}
@组件({
选择器:'应用程序根',
templateUrl:“./app.component.html”,
样式URL:['./app.component.css'],
提供者:[AppService,TimeFromNow],
changeDetection:ChangeDetectionStrategy.OnPush
})
导出类AppComponent{
//属性声明
私人退订:主题=新主题();
构造函数(私有appService:appService,私有timeFromNow:timeFromNow,私有ref:ChangeDetectorRef,fb:FormBuilder){
this.taskForm=fb.group({
“索引”:['',验证程序。组合([])],
“名称”:['',验证程序。组合([])],
'userId':['',Validators.compose([])],
“mobile”:[“”,Validators.compose([])],
“taskDate”:[“”,Validators.compose([])],
});
this.index=this.taskForm.controls['index'];
this.name=this.taskForm.controls['name'];
this.userId=this.taskForm.controls['userId'];
this.mobile=this.taskForm.controls['mobile'];
this.taskDate=this.taskForm.controls['taskDate'];
this.setTableSearchConfig();
}
恩戈尼尼特(){
这个.addBulkTasksOnLoad();
}
恩贡德斯特罗(){
this.unsubscribe.next(true);
此为.unsubscribe.complete();
this.unsubscribe.unsubscribe();
}
addBulkTasksOnLoad(){
this.busy=this.appService.addTaskOnLoad().subscribe((res:any)=>{
这个.loadTable();
},(错误:任何)=>{
});
}
addTask(taskForm:any){
这个.taskPostCount++;
this.appService.addTask(taskForm).takeUntil(this.unsubscribe).subscribe((res:any)=>{
this.addTaskResponse=res;
},
错误=>{
this.addTaskResponse=err;
});
}
加载表(分页事件?:分页事件,分拣事件?:分拣事件,过滤事件?:过滤事件){
this.appService.getTable(this.tableSearchConfig).takeUntil(this.unsubscribe).subscribe((res:any)=>{
for(资源数据的常量历史){
history.updatedAt=this.timeFromNow.transform(history.updatedAt);
}
此表=分辨率数据;
this.setTableSearchConfig(paginateEvent、sortEvent、filterEvent、this.tableSearchConfig.pageNumberToRequest、res.totalRecords);
此.ref.detectChanges();
});
}
.
.
.
.
}
这是内存泄漏的情况吗?如果是,我到底做错了什么,或者在大量使用应用程序后内存增加是正常行为?应用程序的帧速率在最后也显著下降。这肯定是内存泄漏,但应用程序需要详细分析,以找出导致此问题的原因。对于您的在这种情况下,我们可以看到堆大小增加,但为了知道到底是什么导致了泄漏或泄漏在哪里……您需要扩展标识符,并查看哪些对象仍保留在内存中(引用了作为应用程序的父节点)这将帮助您追踪导致这些内存泄漏的代码片段(dom或JS) 有很多好的实践可以防止你的应用程序泄露那么多信息……你可以从中找到一些(这是针对angularjs的,但同样的概念也适用) 我还发现,在一个单独的状态包装器(如ngrx/store)中隔离状态是很有用的,这样您就可以确保它不是您正在提示的数据的大小,因为使用状态,您可以清楚地看到状态对象的大小,以及清楚地、明确地改变(不是字面上的)或向状态对象添加内容 此外,变更策略不会对其产生太大影响(如果它确实对其产生影响的话)内存堆的大小,但只是有助于在内存堆中保存程序,以查看应用程序模型是否已更改,从而影响DOM中的更改。但它保持内存堆不变。因此,它与清除泄漏无关 展开每个标识符以查找哪些DOM节点已分离且仍保留在堆中!然后您可以设计如何针对泄漏进行设计…例如使用ngIf或任何其他角度指令调用它们所连接的组件上的onDestroy(垃圾收集DOM节点或对象)