Search code examples
c#memory-leaksdependency-injectionsimple-injector

Simple injector - decorators causing container to not be garbage collected?


I've noticed that when I add decorator(s) via simple injector, that the container won't go out of scope. Does anyone know if this is expected? If I don't add the decorators, the container is Garbage collected. Otherwise, it's not. Is there anything I should be doing to force cleanup?

Here is a simple example. If the decorators are added, the containers are still in memory after 'Run' has completed. If the decorators are not added, the containers get GC'ed.

public interface IDoSomething { }
public class DoSomething: IDoSomething { }
public class DoSomethingDec1: IDoSomething {
    public DoSomethingDec1(IDoSomething handler) { }
}
public class DoSomethingDec2 : IDoSomething {
    public DoSomethingDec2(IDoSomething handler) { }
}

static void Main(string[] args) {
    Console.WriteLine("Press a key to start");
    Console.ReadKey(true);
    Run();
    GC.Collect();
    Console.WriteLine("Done");
    Console.ReadKey(true);
}

static void Run() {
    //can be re-created with 1 container; easier to see with multiple
    for (var i = 0; i < 1000; i++) {
        using (var container = new Container()) {
            container.Register<IDoSomething, DoSomething>();

            //Comment out these 2 lines to remove the decorators
            container.RegisterDecorator<IDoSomething, DoSomethingDec1>();
            container.RegisterDecorator<IDoSomething, DoSomethingDec2>();

            container.Verify();

            container.GetInstance<IDoSomething>();
        }
    }
}

With decorators:

enter image description here

Without decorators:

enter image description here


Solution

  • UPDATE: This issue is fixed in v3.1.1.

    The problem is caused by a bug in the .NET framework that keeps cyclic object graphs alive if the graph contains a ThreadLocal<T>. In the case of Simple Injector, the decorator subsystem adds a ThreadLocal<T> to the Container's Items dictionary. The ThreadLocal's value indirectly references the Container again, causing the graph to become cyclic. Due to the bug in .NET, the ThreadLocal<T> is thought to be alive, keeping its depending object, such as the Container alive. The Container instance again keeps everything else alive, which obviously causes a memory leak.

    Although we could wait for Microsoft to fix this, this will likely take way too long. So our plan is to address this issue in a future patch release. We will likely replace .NET's ThreadLocal<T> with our own custom implementation that doesn't cause the memory leak. This custom implementation is already used in Simple Injector's PCL library, since ThreadLocal<T> is not available for PCL. This implies that the PCL version of Simple Injector does not have this problem.