I am trying to make sure instances of a class using Rx don’t leak. I have reduced the problem down to the bare essentials and it looks like the Window function is key to the problem.
Consider the following:
<!-- language: c# -->
using System;
using System.Reactive.Linq;
class Program
{
static void Main(string[] args)
{
var foo = new Foo();
Console.WriteLine("Press any key...");
Console.ReadKey(true);
foo = null;
GC.Collect();
Console.WriteLine("Press any key...");
Console.ReadKey(true);
}
}
public class Foo
{
private event EventHandler MyEvent;
public Foo()
{
var subs = Observable.FromEventPattern(
e => MyEvent += e, e => MyEvent -= e);
// (1) foo is never GC'd
subs.Window(TimeSpan.FromSeconds(1)).Subscribe();
// (2) foo is GC'd
//subs.Window(TimeSpan.FromSeconds(1));
// (3) foo is GC'd
// subs.Window(1);
}
~Foo()
{
Console.WriteLine("Bye!");
}
}
When I apply the Window function with a TimeSpan opening selector and subscribe to it, (1) foo is never GC’d.
If I don’t subscribe (2), or I use a different opening selector (3), then it is.
Also, if I use a cold observable as the source then foo is GC’d regardless.
Why is the Window function with a TimeSpan special, and how do I make sure foo will be GC’d when using it?
That looks correct to me.
You are missing the allocation of the subscription to an IDisposable field. You dont dispose of the subscription, and you dont call GC.WaitForPendingFinalizers();
You can fix the test up with the following:
The test can now become
I hope that helps. Also check out the Lifetime Management post on my intro to Rx blog series.
UPDATE: My online book at IntroToRx.com replaces the blog series. Most relevant here seems to be the Lifetime management chapter.