Home .NET About Garbage Collector,Unity and weak links

About Garbage Collector,Unity and weak links

by admin

I have some interface in my project IT and factory method like

interface IT{}public ITCreateT(IAa, IBb, ICc, Type concreteType){// a bunch of shit code that creates an object of type concreteType, defined at the point of call in the runtime.}

Classes implementing IT , have constructors with different signatures that accept some combination of object types IA , IB or IC , so as the number of implementations increases IT , the code to create them started to smell more and more, and finally it was decided to throw it out and replace it with simple code from Unity, along the lines of :

private static IT CreateITInternal(IAa, Type targetType){using (UnityContainer cont = new UnityContainer()){cont.RegisterInstance<IA> (a, new ExternallyControlledLifetimeManager());cont.RegisterType(typeof(IT), targetType, new ExternallyControlledLifetimeManager());return cont.Resolve<IT> ();}}

(for simplicity, I will only leave the references to IA so as not to overload the code. We only need Unity here to ease the instantiation of objects and we don’t want to let it control the lifetime of any objects, so we used ExternallyControlledLifetimeManager )
The code is written, tests are written, everything is green, everything works, roll it out to production. Here it goes…
The calling code looked like this

private static IT CreateIT(){var a = new A();Type concreteType = typeof(T);return CreateITInternal(a, concreteType);}static void Main(string[] args){for (int i = 0; i < 1000; ++i){CreateIT();}}

classes and interfaces

public interface IA { };public interface IT { };public class A : IA{}public class T : IT{public T(IA a){}}

It seems to be OK, both debug and release work fine in Visual Studio. But if I go and run the .exe file, it stably crashes with this exception:

Unhandled Exception: Microsoft.Practices.Unity.ResolutionFailedException: Resolution of the dependency failed, type = "WeakRefTest.IT", name = "(none)".Exception occurred while: while resolving.Exception is: InvalidOperationException - The current type, WeakRefTest.IA, is an interface and cannot be constructed. Are you missing a type mapping?-----------------------------------------------At the time of the exception, the container was:Resolving WeakRefTest.T, (none) (mapped from WeakRefTest.IT, (none))Resolving parameter "a" of constructor WeakRefTest.T(WeakRefTest.IA a)Resolving WeakRefTest.IA, (none)---> System.InvalidOperationException: The current type, WeakRefTest.IA, is an interface and cannot be constructed. Are you missing a type mapping?

Stack trace

at Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy.ThrowForAttemptingToConstructInterface(IBuilderContext context)at lambda_method(Closure , IBuilderContext )at Microsoft.Practices.ObjectBuilder2.DynamicBuildPlanGenerationContext.<> c__DisplayClass1.<GetBuildMethod> b__0(IBuilderContext context)at Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context)at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context)at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context)at Microsoft.Practices.ObjectBuilder2.BuilderContext.NewBuildUp(NamedTypeBuildKey newBuildKey)at Microsoft.Practices.Unity.ObjectBuilder.NamedTypeDependencyResolverPolicy.Resolve(IBuilderContext context)at lambda_method(Closure , IBuilderContext )at Microsoft.Practices.ObjectBuilder2.DynamicBuildPlanGenerationContext.<> c__DisplayClass1.<GetBuildMethod> b__0(IBuilderContext context)at Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context)at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context)at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context)at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides)--- End of inner exception stack trace ---at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides)at Microsoft.Practices.Unity.UnityContainer.Resolve(Type t, String name, ResolverOverride[] resolverOverrides)at Microsoft.Practices.Unity.UnityContainerExtensions.Resolve[T](IUnityContainer container, ResolverOverride[] overrides)at WeakRefTest.Program.CreateITInternal(IA a, Type targetType)at WeakRefTest.Program.CreateIT()at WeakRefTest.Program.Main(String[] args)

wat? We literally added the object to the container two lines above…
All sorts of logging has shown that everything works as it should. The objects do register in the container, and things don’t crash on the first iteration.
A brief rereading of the documentation yielded a suspect : ExternallyControlledLifetimeManager , internally it stores weak references to objects placed in the container, and possibly , if between placing an object of type IA into the container and a request to construct an object of type IT it will be destroyed, then the construction should fall just with a similar exception. But on the other hand, an object of type IA can only be deleted if the weak reference from our container is the only one, but in our case it is not! And in CreateITInternal and in CreateIT has strong references to this object on the stack, which should prolong its life at least until it exits CreateIT Or is it not? Checking :

private static IT CreateITInternal(IA a, Type targetType){using (UnityContainer cont = new UnityContainer()){cont.RegisterInstance<IA> (a, new ExternallyControlledLifetimeManager());cont.RegisterType(typeof(IT), targetType, new ExternallyControlledLifetimeManager());GC.Collect();return cont.Resolve<IT> ();}}

Now it crashes on the first iteration. I.e. it’s really about garbage collection and weak links.
A simpler example :

private static void TestWeakRef(){var sa = new A();var wa = new WeakReference<A> (sa);GC.Collect();A sa2;wa.TryGetTarget(out sa2);Console.WriteLine("{0}", sa2 == null ? "null" : "not null");Console.ReadLine();}

In Visual Studio it gives out "not null" when run outside of studio it gives out "null".
Apparently, having strong references in the code doesn’t prolong the lifetime of an object before those references leave the scope, it’s the actual dereferencing of those references that matters.
The fix is extremely simple :

private static IT CreateITInternal(IA a, Type targetType){using (UnityContainer cont = new UnityContainer()){cont.RegisterInstance<IA> (a, new ExternallyControlledLifetimeManager());cont.RegisterType(typeof(IT), targetType, new ExternallyControlledLifetimeManager());IT ret = cont.Resolve<IT> ();GC.KeepAlive(a);return ret;}}

So when using weak links, watch out, the garbage collector may be more aggressive than you expect.
It’s still not clear, though, why it always works when running in VS.

You may also like