Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do I need to unsubscribe from an Observable if the Observable is finished with?

Let us say I have an Observable (hot, doesn't complete), and I subscribe to it. Ordinarily when I am finished with the Subscription I have to unsubscribe it to prevent memory leaks.

let subject$ = new Subject();

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

But if instead of just being finished with the Subscription I am also finished with the Observable (Subject) and I remove all reference to both, do I need to call unsubscribe method?

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?

My logic is telling my I don't because both the Subject and the Subscription are now eligible for garbage collection, and will be destroyed, even though they reference each other. Or is there some hidden reference I don't know about?

Don't worry about the difference between using unsubscribe, takeUntil or other mechanisms.

like image 203
DJClayworth Avatar asked Oct 20 '25 09:10

DJClayworth


1 Answers

In the case of a let subject$ = new Subject(); clearing the references to the Subject and the Subscription will be enough, everything will be garbage collected after that.

The risk of a memory leak gets real when you are subscribing to the Subject in an object, and you don't unsubscribe from the Subject before clearing all the references on the object. In that case, the whole object will remain active and won't be garbage collectable.

Let's take this example:

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;

In this example, when clearing all the references on bigObject, the subject still has a reference on the x => this.result = x callback which has a reference on bigObject, making it uncollectable as a whole.

Either by unsubscribing, or clearing the subject, this will break the references chain that keeps bigObject alive, and it will be eligible for garbage collection.

To observe the behavior by yourself, you can copy the content of this file https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js in your console, then copy paste the example code. You will notice a memory increase in the task manager. When creating a heap dump in the Memory tab in the developer tools, you'll be able to find the 4 objects by typing BigClass in the search field.

After that, type subject = null; in the console, then create a new heap dump. You'll notice that the 4 objects have disappeared.

As a conclusion, as long as an Observable is destroyed, these is no real risk of a memory leak because all of the subscriptions will also be destroyed. The risky Observables are those who are permanent (e.g: attached to a global DOM event with fromEvent), and with callbacks referring to objects that need to be destroyed.

like image 73
Guerric P Avatar answered Oct 21 '25 23:10

Guerric P



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!