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:
Without decorators:
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.