John Reynolds
John Reynolds

Reputation: 5057

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

Upvotes: 5

Views: 1779

Answers (2)

Lee Campbell
Lee Campbell

Reputation: 10783

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.

Upvotes: 4

Fyodor Soikin
Fyodor Soikin

Reputation: 80880

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.

Upvotes: 5

Related Questions