提问者:小点点

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


假设我有一个可观察(热,不完整),并且我订阅了它。通常,当我完成Subscription时,我必须取消订阅它以防止内存泄漏。

let subject$ = new Subject();

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

但是,如果不仅仅是完成Subscription,我还完成了可观察主题)并且我删除了对两者的所有引用,我需要调用取消订阅方法吗?

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?

我的逻辑告诉我我不知道,因为主题订阅现在都有资格使用垃圾回收机制,并且将被销毁,即使它们相互引用。或者有一些我不知道的隐藏引用?

不用担心使用取消订阅采取直到或其他机制之间的区别。


共2个答案

匿名用户

let主题$=new主题();的情况下,清除对主题订阅的引用就足够了,之后所有内容都将被垃圾收集。

当您订阅对象中的主题时,内存泄漏的风险变得真实,并且在清除对象上的所有引用之前没有取消订阅主题。在这种情况下,整个对象将保持活动状态,不会被垃圾收集。

让我们举这个例子:

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上的所有引用时,主题仍然对x=有引用

通过取消订阅或清除主题,这将破坏使bigObject存活的引用链,并且它将符合垃圾回收机制的条件。

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

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

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

匿名用户

对于内存使用没有区别。

当你调用sub. un订阅();RXJS唯一做的就是将观察者设置为null,在这里你可以看到原始的RXJS取消订阅代码:

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

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

但是,我建议您坚持使用取消订阅的方式,因为您永远不知道RXJS将在未来的版本中添加什么。例如,他们可能会添加一个新功能,如下所示:

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

在这种情况下,您只需执行subject=null;的方法将导致内存泄漏(因为colNewFeature可能在其他地方有引用)。