ReactiveUI ObservableAsPropertyHelper / Reactive Extensions Memory Leak?
Asked Answered
R

1

6

I noticed that in my .NET 3.5 application, which uses ReactiveUI, I have a significant memory leak that seems to originate in ObservableAsPropertyHelper. I created a test project to demonstrate it here.

It seems that every change notification triggered by a simple ObservableAsPropertyHelper calculated property leaks memory. The leak seems to originate in Reactive Extensions, not directly in ReactiveUI, but my use of OAPH is so simple that I wonder if anyone has encountered this or may have a proposed fix.

The severity of the memory leak varies between .NET 3.5 (RxUI 2.4, Rx 1.1) and .NET 4.0 (RxUI 4.2, Rx 2.0.3). It is much closer to linear with every update of the property in .NET 3.5. However, the leak is still there in .NET 4.0.

I have uploaded the test project and some profiler images for my .NET 3.5 and .NET 4.0 test session with the test application, here.

You can see in the images that the object graphs are different, so we may be talking about two different leaks entirely. In the 4.0 session (40_RetentionGraph.png) you can see that the most allocated objects are Ints (the type of my OAPH property) and ConcurrentQueue. There seems to be some kind of circular reference issue going on there. You can also see in 40_IntsAllocatedGCRootGrows.png that the distance from GC root of the instances grows.

In the 3.5 version (which I'm most concerned about), you can see (35_Summary.png) that the most allocated objects are Action and ScheduledObserver. The object graph is a bit more complex and altogether different than the 40 version.

I've reviewed this discussion but haven't found a direct answer: my scenario is, very simple updates to OAPH. Any insight on possible solutions to this leak is appreciated.

Romy answered 21/5, 2013 at 11:0 Comment(0)
N
5

You haven't specified a scheduler, so by default RxUI is going for the DispatcherScheduler. Since your app is a console app there is no running Dispatcher. All that memory is being consumed by the OnNext's queueing up with nothing to run them.

You could mess about with firing up a dispatcher and feeding it frames to run (which a WPF app would do for you), or for the sake of testing just change the ReactiveTester constructor from this:

public ReactiveTester()
{
  _Total = this.WhenAny(x => x.A, x => x.B, (a, b) => a.Value + b.Value)
               .ToProperty(this, x => x.Total);
}

To this:

public ReactiveTester()
{
  _Total = this.WhenAny(x => x.A, x => x.B, (a, b) => a.Value + b.Value)
               .ToProperty(this, x => x.Total, 0, Scheduler.CurrentThread);
}

And everything will be just rosy.

Nganngc answered 21/5, 2013 at 18:44 Comment(8)
Yep, I suspected this in the RxUI bug that is filed. Good work.Penicillate
You're right, adding this solves the problem in the test app. Thank you James and Paul!. I guess my followup question is: what could be causing a similar leak in my WPF app? My .WhenAny()'s are called from UI as well as background threads. Do I need to explicitly specify a scheduler in every .ToProperty() call? If so, should it be Scheduler.CurrentThread?Romy
I'm speculating without seeing your code, but I see your 3.5 sample is using RxUI and Rx 1.1. IIRC, that combination will result in a new Dispatcher being created (that won't get pumped) if you call ToProperty() on a non-UI thread and don't specify a scheduler. Try run your initialization code on the Dispatcher.Nganngc
You should really only be calling ToProperty in the constructor of your ViewModels and you should only be creating ViewModels on the UI thread (though that 2nd one isn't always true, it's a Good Idea™, especially in this case)Penicillate
"IIRC, that combination will result in a new Dispatcher being created" - interesting, didn't know that.Penicillate
You imperatively load the DispatcherScheduler type and reflect the Instance property, that property returns a new DispatcherScheduler initialized with Dispatcher.CurrentDispatcher, which will create a new Dispatcher if one isn't already associated with the thread.Nganngc
I'm looking at RxUI v2.4 in the RxApp class btw.Nganngc
Create ViewModels and make sure ToProperty() is called on the UI thread. Got it, and thanks again @JamesWorld and Paul.Romy

© 2022 - 2024 — McMap. All rights reserved.