Memory Usage, Autofac and Transient Objects

Halfwit 2 makes heavy use of dependency injection using the awesome Autofac IoC container. Almost every ViewModel is resolved as a dependency by Autofac. For example, check out the constructor on the home timeline's ViewModel class:

public HomeViewModel(
    Dispatcher dispatcher,
    IMessenger messenger, 
    MainViewModel host,
    Func<TimelineViewModel, ITweetable, StatusViewModel> statusFactory,
    Func<StatusViewModel, ConversationViewModel> conversationFactory,
    Func<TimelineViewModel, TweetViewModel> tweetUpdateFactory,
    Func<TimelineViewModel, IHalfwitUser, DirectMessageViewModel> messageUpdateFactory,
    IEnumerable<TwitterPoller> poller)

Everything that the home timeline needs is passed into the constructor, either explicitly or in the form of a Func so the timeline can generate multiple instances of an object on the fly.

One parameter in particular is noteworthy here:

Func<TimelineViewModel, ITweetable, StatusViewModel> statusFactory,

That's the factory function that the timeline uses to create tweets. Each time a new set of tweets come in, the home timeline will call that function and generate a new StatusViewModel instance for every tweet.

There's a problem with this code, though, and it's one I wasn't aware of until just a few days ago.

Autofac, by default, tracks the instances of every object it creates. That means that the container itself holds a reference to every instance, and those instances therefore cannot be garbage collected until the container itself is collected. That's true even for factory-generated instances (i.e. when you resolve a Func and use that to generate new objects).

This has a big impact when it comes to the StatusViewModel class we just talked about, since Halfwit only keeps 100 of them around at any time, and expects the older ones to "disappear". It turned out that they weren't disappearing when I removed them from the timeline's list of tweets, because the Autofac container was keeping a reference to them!

This meant that if you left Halfwit 2 running long enough, the memory usage would continually grow. I was seeing instances of halfwit2.exe using as much as 450MB of memory!

The solution is to tell Autofac that you want to "own" the instances and will be responsible for their disposal, and you do that by asking for a special Owned<T> type rather than T directly. Check out this article in the Autofac documentation: Owned Instances.

So our StatusViewModel factory dependency now looks like this:

Func<TimelineViewModel, ITweetable, Owned<StatusViewModel>> statusFactory,

See how we're now asking for a Func that returns Owned<StatusViewModel>? That's how we tell Autofac that we will be in control of each StatusViewModel instance's lifetime. When we remove the tweet from the list, the garbage collector now knows that it can be cleaned up.

The latest version of the Halfwit 2 beta now sits happily around 40MB of memory on my 2GB netbook no matter how long I leave it running for. The amount of memory it uses will vary from PC to PC, since the garbage collector will only bother collecting instances when it decides that there's enough "memory pressure" for it to do so, and that depends on how much RAM you have in your machine.

This is definitely a trap for new players in the world of IoC containers. Not every container does this level of tracking, but it's worth double checking the one you're using, and learning about how to tell the container that you want control over the dependencies' lifetimes.

Update As Nick says in the comments, Autofac actually only tracks objects that implement IDisposable. MVVM Light's ViewModelBase does, so that's why my tweets were being tracked. Still, I would recommend depending on Owned<T> for anything if you want control over its lifetime, regardless of whether it implements IDisposable.

.net wpf autofac halfwit
Posted by: Matt Hamilton
Last revised: 23 Feb, 2012 12:33 AM History

Trackbacks

Comments

10 Jan, 2011 04:23 AM

Good write-up, should be required reading for Autofaccers (Autofackers? Ummm...)

Small correction, Autofac only tracks instances of components that implement IDisposable - but since of course you don't know in advance which components will implement that interface, you should follow the Owned<T> advice regardless.

Cheers!

10 Jan, 2011 04:28 AM

Thanks Nick,

As it turns out, MVVM Light's ViewModelBase class implements IDisposable, which is why Autofac was tracking tweets. As you said, it's probably good practice to do the Owned<T> thing regardless if it's an object whose lifetime you want control over.

Vijay Santhanam
Vijay Santhanam
10 Jan, 2011 04:29 AM

Nicely written Matt! I've been bitten by this too.

Perhaps a wiki entry should be written about it on http://code.google.com/p/autofac/w/list

UncleBook
UncleBook
11 Feb, 2011 08:15 AM

This thread has proved very useful to me also. Thank you all. One question/issue I have though is the recommended pattern of passing an Owned reference into a component that I do NOT want to make smelly with an Autofac dependency.

Is there an issue with the container resolving T as an owned instance internally and passing T back to the dependent type as follows?

In the container =>

    private T ResolveFooService(string bindingName)
    {
        Parameter[] parameters = new Parameter[1];

        parameters[0] = new TypedParameter(typeof(string), bindingName);

        var scope = Container.BeginLifetimeScope();

        T instance = scope.Resolve<Owned<T>>(parameters).Value;

        return instance;
    }
11 Feb, 2011 11:31 AM

I think it's a personal choice, UncleBook. Nick prefers the explicitness of resolving Owned<T> so that readers of the code understand that it's a dependency that isn't longer-lived than the object that's using it, but I've found myself using my own tricks to abstract away the Owned<T> reference similar to what you're doing here.

I can't really advise one approach over the other. If you know what you're doing, you'll be safe.

If Nick's hanging out here still, he might be able to comment on the correctness of your code. Otherwise drop him a comment over on his blog post on this very topic.

UncleBook
UncleBook
11 Feb, 2011 07:40 PM

Thanks very much Matt for your considered and swift response.

Drew
Drew
03 Nov, 2011 12:33 AM

This of course means your code needs to know about the IoC layer. Suddenly your classes are all tied to Autofaq. Code smell.

03 Nov, 2011 02:10 AM

I agree Drew - I'm not a huge fan of taking a dependency on Autofac at this level. UncleBook has offered a workaround above, and there's also a way that Nick once suggested that you can tell Autofac to resolve a certain type by resolving an Owned and returning the value. It's messy, but it would remove the reference to Autofac from your code.

Your Comments

Used for your gravatar. Not required. Will not be public.
Posting code? Indent it by four spaces to make it look nice. Learn more about Markdown.

Preview