New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ReactiveCommand can lead to memory leaks! #20
Comments
Does this repro if your canExecute isn't a Timer? I suspect a circular reference is going on with the timer, which will constantly have a reference because it queues a WorkItem. Since the Timer will only go away when the ReactiveCommand is GC'd and the ReactiveCommand has an indirect reference to the Timer, it stays around forever. Try with canExecute = Observable.Return(true). |
With canExecute = Observable.Return(true) I can also see the leak. |
This isn't a bug - the reason the memory leaks is because your while(true) {} is in the OnStartup, so we never actually pump messages, or more importantly, run the Dispatcher queue items. Every instance of MainWindowViewModel ends up queuing a dispatcher item, which just sits there in the queue (which then has a ref to the ViewModel). Change the code to this and it doesn't repro (the CPU is pinged at 100%, but the memory usage is constant): That's a super-obscure reason why the leak is happening, thanks for reporting it anyways! |
Thanks Paul for finding out what the real cause was. However this does not eliminate my first concern related to ReactiveCommand. I created this Leak project just to highlight the problem of not disposing subscription to observable which is passed in constructor. Some IObservables like Observable.Timer<> require you to remove subscription. Otherwise they are active all the time. Maybe you could choose the option you think fits better and implement it. It's bed practice to ignore Disposable pattern. |
"It's bed practice to ignore Disposable pattern." So in general, you're right - however, Rx is a little different; they're not using IDisposable the same way that the Fx folks use it. The IDisposable returned is just a token - some way to say "I want to unsubscribe" that happens to have some extra language support. The Rx IDisposables don't implement a Finalizer for example. You don't have to Dispose every Subscription when using Rx, nor do you have to keep the IDisposable at all when you're a client (and since the object doesn't actually represent an unmanaged resource, the GC will still handle everything). The one exception to this is when you're implementing a method that returns IObservable, or an operator. In that case, you want to disconnect your "parent" (i.e. your source Observable) as soon as possible - you can see this in action at https://github.com/xpaulbettsx/ReactiveUI/blob/master/ReactiveUI/ObservedChangedMixin.cs#L159 http://stackoverflow.com/questions/5227823/keeping-references-to-idisposable-when-using-the-reactive-extensions-for-net gives more info about this as well as http://blog.aggregatedintelligence.com/2010/10/idisposable-and-finalizers.html And philosophically speaking, many of the Observables you're constructing in RxUI are kind of the equivalent of Bindings, you don't want them to detach early (with the exception of things in ItemsControls). |
Thanks for pointing me to some useful materials. Now I understand that I don't need to save IDisposable I get from Subscribe methods. Nevertheless, there is slight difference between 'don't need' and 'can't'. Imagine a situation when you periodically poll a database/service to check if you can execute some operation. You would probably use Observable.Timer. Unfortunately this Observable gets attached to Scheduler and if you don't remove subscription (Dispose) it won't be garbage collected. Moreover, it will hold a reference to your ReactiveCommand and its object graph. This for example results in memory leak: I don't claim that you have to dispose everything but it would be nice if ReactiveUI users had choice. |
"I don't claim that you have to dispose everything but it would be nice if ReactiveUI users had choice." I agree - RxUI was written as I myself was learning Rx, so many pllaces don't have the best development practices. I'm going to make a pass through the code to fix these kinds of bugs. Great discussion! |
…is a pass through update to an injected dependency or object contained within the class. See usage below using the decorator pattern as an example. (#20) + Added new ReactiveDependencyPropertyWeaver which supports targeting properties one level deep on a named property or field. + Added unit tests for decorator pattern and facades + Updated ModuleWeaver to use new weaver Sample Usage ```C# public class Thing : IThing { [Reactive] public virtual string CoolProperty { get; set; } } public class DecoratedThing : Thing { private Thing _thing; public DecoratedThing(Thing thing) { _thing = thing; } // BEFORE public override string CoolProperty { get { return _thing.CoolProperty; } set { _thing.CoolProperty = value; this.RaisePropertyChanged("CoolProperty"); } } // AFTER [ReactiveDependency(nameof(_thing)] public override string CoolProperty { get; set; } } ```
Hi Paul,
I noticed a place where you can have memory leaks and tried to fix it. I thought that implementing IDisposable will solve the problem. Unfortunately it didn't help. I assume it's something related to your caching (momoization).
You can find Leak project in my branch:
git@github.com:StanislawSwierc/ReactiveUI.git
The text was updated successfully, but these errors were encountered: