如何在非常小的Angular应用中识别内存泄漏

时间:2019-04-12 09:08:19

标签: angular rxjs angular-httpclient

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

当前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消耗问题。

2 个答案:

答案 0 :(得分:1)

目前尚不清楚泄漏的是什么(以及是否泄漏),因此很难建议一些特殊的东西。但是这里有一些提示:

1)尝试删除不必要的Subjects,您只需向视图公开可观察的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可能需要500msexhaustMap将处理反向压力,但您应添加一个timeout以限制请求时间,并确定添加一些错误处理,因为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;
  })
)

更复杂的方法可能是将timeoutretry结合起来。

除此之外,http响应本身可能是错误的或不是json,这将导致错误。因此,必须在这里进行错误处理。

这里有一个more detailed overview of error handling in rxjs

Stackblitz example for above said

侧面注意事项:您不知道24/7全天候运行一个月会发生什么。因此,您当然也想在系统中添加一些日志记录。只是为了学习,如果失败了。

答案 1 :(得分:0)

根据rxjs docs

  

interval返回一个Observable,它发射无限个递增的整数序列,并在这些发射之间选择一个固定的时间间隔。第一次发射不会立即发送,而只会在第一个周期过去之后发送。默认情况下,该运算符使用异步SchedulerLike来提供时间概念,但是您可以将任何SchedulerLike传递给它。

我认为问题出在这里:

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

基本上,每200毫秒就要创建一个新的subscribe函数。这些对象很可能永远不会进行垃圾处理,因此它们加起来会占用内存。

我建议查看代码,以便在收集到回复后正确地退订。

或者,如果您可以控制API服务器,那么我一定会使用Web套接字。看看socket.io,它使启动简单的套接字服务器变得非常简单。