Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rx subscriptions and garbage collection

Do you need to assign the IDisposable returned by IObservable.Subscribe to a variable in order to protect the subscription from being garbage collected, or is the presence of an active subscription enough?

My use case: I create a one-shot observable from an existing observable (myObservable in the example):

myObservable.Take(1).Subscribe(fun v -> printfn "One-shot: %A" v) |> ignore
like image 590
John Reynolds Avatar asked Jan 06 '23 09:01

John Reynolds


2 Answers

Yes, the presence of active subscription is enough. The GC-preventing chain of references to your subscription starts ultimately at the source of the very first observable, so while the stream source is alive, your subscription is also alive. If the stream source itself gets collected, then your subscription will die with it, but that's ok, because it wouldn't ever be invoked again anyway.

On the flip side, once your subscription receives one pulse, the .Take(1) implementation will disconnect it from the source, allowing it to be collected.

like image 103
Fyodor Soikin Avatar answered Jan 11 '23 15:01

Fyodor Soikin


In Rx.NET 2.2 there are no usages of Finalisers, so your subscription will never be disposed just because it got garbage collected.

If you do not assign the subscription to a variable and explicity dispose of it, it will continue to run until it terminates (OnComplete/OnError). This is outlined here - http://introtorx.com/Content/v1.0.10621.0/03_LifetimeManagement.html#Finalizers

Thus by not assigning the subscription to a variable, you loose the ability to Dispose of the subscription early. i.e. if a user wanted to cancel the action before a result was returned, you will have lost this ability.

An proof of this behaviour (in C#, sorry)

var myObservable = Observable.Timer(TimeSpan.FromSeconds(1));
myObservable.Take(1).Subscribe(v => Console.WriteLine($"One-shot: {v}"));

//Force full GC.
GC.Collect();
//Wait for any Finalizers
GC.WaitForPendingFinalizers();
//Then clear out anything kept by finalizers.
GC.Collect();

//We will still see "One-shot: 0" written to the console.
like image 32
Lee Campbell Avatar answered Jan 11 '23 17:01

Lee Campbell