首页 > 解决方案 > 如果 Observable 完成了,我是否需要取消订阅 Observable?

问题描述

假设我有一个Observable(热门,未完成),并且我订阅了它。通常,当我完成后,Subscription我必须取消订阅以防止内存泄漏。

let subject$ = new Subject();

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

但是,如果不仅完成了Subscription我还完成了Observable( Subject) 并且我删除了对两者的所有引用,我是否需要调用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?

我的逻辑告诉我我没有,因为 theSubject和 theSubscription现在都有资格进行垃圾收集,并且将被销毁,即使它们相互引用。还是有一些我不知道的隐藏参考?

不要担心 using 或其他机制之间的unsubscribe区别takeUntil

标签: javascriptmemory-leaksrxjs

解决方案


如果let subject$ = new Subject();清除对Subject和的引用Subscription就足够了,之后所有内容都将被垃圾收集。

当您订阅对象时,内存泄漏的风险变得真实,并且您在清除对象上的所有引用之前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;

在这个例子中,当清除所有对 的引用时bigObjectsubject仍然有一个对回调的引用,该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 个对象已消失。

总而言之,只要 anObservable被销毁,就没有真正的内存泄漏风险,因为所有订阅也将被销毁。有风险Observables的是那些永久的(例如:附加到全局 DOM 事件fromEvent),并且回调引用需要销毁的对象。


推荐阅读