首页 > 解决方案 > 如何识别非常小的 Angular 应用程序中的内存泄漏

问题描述

我目前正在开发一个 Angular 应用程序,该应用程序应该 24/7 运行至少一个月(制造软件)。客户端仅接受每月仅发生一次浏览器重启(维护间隔)。我们实现的第一个用例只包含一个向用户显示一些信息的组件。此时没有用户交互!信息从服务器推送到客户端。目前我只是从服务器轮询数据更新并向用户显示信息。

当前的 200ms 间隔只是为了研究目的,在实际场景中它将是 1000ms。下面的代码导致 Chrome 在 3 小时内增加了大约 40MB 的内存,并且 cpu 使用率增加了高达 50%(消耗两个核心之一)。

推送通知的目标技术是 SignalR。由于我使用 SignalR 发现了内存问题,因此此处提供的轮询实现用于调查 SignalR 库是否是问题所在。不幸的是,我在这里遇到了同样的问题。

当然,每 30 分钟执行一次 window.location.reload() 可以“解决”问题,但这不是一个好的解决方案。如果我在 3 小时后执行重新加载,页面就会崩溃,Chrome 会显示“哦,不……崩溃”。我正在使用 Chrome 73 和 Edge,Edge 的内存增加明显高于 Chrome。使用 Angular 7.2

<div *ngIf="info" style="width: 100%; height: 100%; margin: 0 auto;">
  <div class="info status{{ info.Status }}">
    <div class="location"><p class="font2">{{ info.Location }}</p></div>
    <!-- ... further div elements here but no other *ngIf or *ngFor -->

        <div *ngIf="info?.Details" class="no-padding">
          <div class="column1 no-padding" *ngFor="let item of info.Details">
            <div class="inverse"><p class="font1">{{ item.Name }}</p></div>
          </div>
        </div>
        <img class="icon" src="assets/Icon{{info.Icon}}.png"/>
    </div>
  </div>
</div>
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription, interval, Subject } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { Info } from '../data/info';

@Component({
  selector: 'app-info',
  templateUrl: './info.component.html',
  styleUrls: ['./info.component.scss']
})
export class InfoComponent implements OnInit, OnDestroy {
  subscribetimer: Subscription;
  subscribelistener: Subscription;
  listener: Subject<Info>;
  info: Info;

  constructor(private http: HttpClient) { }

  ngOnInit() {
    this.listener = new Subject<Info>();
    this.subscribelistener = this.listener.subscribe(unit => this.info = unit);

    this.subscribetimer = interval(200)
      .subscribe(data => {
        this.http.get<Info>(`http://localhost:5000/poll`)
            .subscribe(res => this.listener.next(res));
      });
  }

  ngOnDestroy() {
    this.subscribetimer.unsubscribe();
    this.subscribelistener.unsubscribe();
  }
}

我希望我可以 24/7 全天候运行这个小型应用程序至少一个月,而不会出现内存和 CPU 消耗问题。

标签: angularrxjsangular-httpclient

解决方案


目前尚不清楚泄漏的是什么(以及是否泄漏),因此很难建议一些特别的事情。然而,这里有一些提示:

1)尝试删除不必要Subjects的,您可以将一个 Observable 暴露info$给视图:

export class AppComponent  {
  info$: Observable<IData>;

  constructor(private http: HttpClient) { }

  ngOnInit() {
    this.info$ = interval(200)
      .pipe(
        exhaustMap(() =>
          this.http.get<Info>(`http://localhost:5000/poll`)
        )
      );
  }
}

在视图中,类似于:

<div *ngIf="info$ | async as info">
  <div *ngFor="let item of info.items">
    {{item}}
  </div>
</div>

2)您可能在 http-get 中有大量超时,例如,您的计时器每滴答一次200ms,而 http-get 可能需要500ms. exhaustMap将处理背压,但您应该添加 atimeout以限制请求时间并肯定添加一些错误处理,因为 http-get 会有错误。一个非常基本的例子:

this.http.get<Info>(`http://localhost:5000/poll`).pipe(
  // killing requests taking too long
  timeout(400),
  // some error handling logic should go here
  catchError(error => {
    return EMPTY;
  })
)

更复杂的方法可能有 atimeout和 a retry

除此之外,http 响应本身可能是错误的或非 json 的,这将导致错误。所以这里的错误处理是必须的。

这是 rxjs 中错误处理的更详细概述

Stackblitz 上面说的例子

旁注:您不知道在 24/7 运行一个月时会发生什么。所以你肯定也想在你的系统中添加一些日志记录。只是为了能够学习,如果它失败了。


推荐阅读