如果可观察对象完成,是否需要取消订阅可观察对象?

时间:2020-01-08 20:57:12

标签: javascript memory-leaks rxjs

让我们说我有一个Observable(很热,没有完成),我已经订阅了它。通常,在完成Subscription后,我必须取消订阅它,以防止内存泄漏。

let subject$ = new Subject();

const sub = subject$.subscribe(...);
...
// Need to call sub.unsubscribe() when we are finished
sub.unsubscribe();
sub = null;

但是,如果我不仅完成了Subscription,而且还完成了ObservableSubject),并且删除了对这两者的所有引用,我是否需要致电{{ 1}}方法?

unsubscribe

我的逻辑告诉我我不这样做,因为let subject$ = new Subject(); const sub = subject$.subscribe(...); ... sub = null; subject$=null; // Assume I have no other references to these // Do I need to call sub.unsubscribe() in this case? Subject现在都可以进行垃圾收集,并且即使它们互相引用也将被销毁。还是有一些我不知道的隐藏参考?

不用担心使用Subscriptionunsubscribe或其他机制之间的区别。

2 个答案:

答案 0 :(得分:2)

如果let subject$ = new Subject();清除对SubjectSubscription的引用就足够了,此后所有内容都会被垃圾回收。

当您在对象中订阅Subject时,内存泄漏的风险变得很严重,并且在清除对象上的所有引用之前,您都不会退订Subject。在这种情况下,整个对象将保持活动状态,并且不会被垃圾回收。

让我们举个例子:

class BigClass {
    constructor(observable) {
        this.bigArray = new Array(9999999).fill(0);
        observable.subscribe(x => this.result = x);
    }
    //...
}

let subject = new rxjs.Subject();
let bigObject = new BigClass(subject);
let bigObject1 = new BigClass(subject);
let bigObject2 = new BigClass(subject);
let bigObject3 = new BigClass(subject);

bigObject = null;
bigObject1 = null;
bigObject2 = null;
bigObject3 = null;

在此示例中,清除bigObject上的所有引用时,subject仍在x => this.result = x回调上具有引用,而该回调在bigObject上具有引用,整体上无法收集。

通过取消订阅或清除subject,这将破坏使bigObject保持有效的引用链,并且有资格进行垃圾回收。

要自己观察行为,可以在控制台中复制此文件https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js的内容,然后复制并粘贴示例代码。您会注意到任务管理器中的内存增加。在开发人员工具的“内存”选项卡中创建堆转储时,您可以通过在搜索字段中输入BigClass来找到4个对象。

然后,在控制台中键入subject = null;,然后创建一个新的堆转储。您会注意到这4个对象已消失。

结论是,只要Observable被销毁,这就不会造成内存泄漏的真正风险,因为所有订阅也会被销毁。有风险的Observables是永久存在的对象(例如,使用fromEvent附加到全局DOM事件),并且具有指向需要销毁对象的回调。

答案 1 :(得分:0)

否,您不需要

对于内存使用没有什么区别。

调用sub.unsubscribe();时,RXJS唯一要做的就是将观察者设置为nullhere you can see the original RXJS unsubscribe code

  unsubscribe() {
    this.isStopped = true;
    this.closed = true;
    this.observers = null!;
  }

这些标志仅用于进一步的验证。

但是我建议您坚持使用unsubscribe的方式,这是因为您永远都不知道 RXJS将在将来的版本中添加什么。例如,他们有可能添加新功能,例如:

  unsubscribe() {
    this.isStopped = true;
    this.closed = true;
    this.observers = null!;
    if (this.coolNewFeature) {
        this.coolNewFeature.unsubscribe()
    }
  }

在这种情况下,您仅执行subject = null;的方法将导致内存泄漏。