Skip to content

Memory leak when singleton in child container depend on singleton in parent via an interface #113

Closed
@jo-hel

Description

@jo-hel

Description

There seem to be a memory leak if a singleton registered in a child container depend on a singleton in the parent container via an interface. See example.

Using parentContainer.RegisterType<IA, A>(new ContainerControlledLifetimeManager()); solves the memory issue, but causes IA and A to resolve to different objects.

I would have expected some way to register both IA and A so they resolve the same object, and that all objects in a child container to be released when that child container is disposed.

Discovered this problem when upgrading from 4.0.1 to 5.8.6, but 5.8.11 has the same problem.

To Reproduce

    public interface IA { }
    class A : IA { }
    class B 
    {
        public IA A { get; }
        public B(IA a)
        {
            A = a;
        }
    }

    class Program
    {
        
        static void Main(string[] args)
        {
            using (var parentContainer = new UnityContainer())
            {
                parentContainer.RegisterType<A>(new ContainerControlledLifetimeManager());
                parentContainer.RegisterType<IA, A>();
                using (var childContainer = parentContainer.CreateChildContainer())
                {

                    childContainer.RegisterSingleton< B>();
                    var b = childContainer.Resolve<B>();
                    Console.WriteLine("ia == a: " + (childContainer.Resolve<IA>() == childContainer.Resolve<A>()));
                }
                // B is kept alive by the parentContainer
                Console.WriteLine("Press any key"); 
                Console.ReadKey();
            }
        }
    }

Additional context

Creating a memory snapshot after the child container has been disposed reveals the B object still being alive. dotMemory reports the retention path as following:

Retention path of UnityMemoryLeak.B

Unity.UnityContainer._registrations ->
Unity.Storage.HashRegistry<System.Type, Unity.Storage.IRegistry<System.String, Unity.Policy.IPolicySet>>.Entries ->
Unity.Storage.HashRegistry+Entry<System.Type, Unity.Storage.IRegistry<System.String, Unity.Policy.IPolicySet>>[37] at [5].Value ->
Unity.Storage.LinkedRegistry.Value ->
Unity.Registration.ContainerRegistration.Next ->
Unity.Storage.LinkedNode<System.Type, Unity.Policy.IBuilderPolicy>.Value ->
Unity.ObjectBuilder.BuildPlan.DynamicMethod.DynamicMethodBuildPlan._buildMethod ->
Unity.ObjectBuilder.BuildPlan.DynamicMethod.DynamicBuildPlanMethod._target ->
Unity.Strategies.BuildKeyMappingStrategy+<>c__DisplayClass0_0.context ->
Unity.Builder.BuilderContext.k__BackingField ->
Unity.Builder.BuilderContext.k__BackingField ->
UnityMemoryLeak.B

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions