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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With