Memoryleak if a singleton never accesses a lazy dependency

Issue #350 new
Deeem2031 created an issue

When an object that is registered as singleton has a lazy dependency which is never accessed the application will leak memory. I can replicate this with Delphi 10.2 and 10.4 with the latest develop version of Spring4D (ecb65cf0da6177c0a3da4ca6610efdc902215808).

Not using .AsSingleton or always accessing the lazy does fix the problem but I see no reason it shouldn’t work the way I intended to use it.

The leak information looks like this:

5 - 12 bytes: Unbekannt x 6
13 - 20 bytes: Unbekannt x 3
21 - 28 bytes: LazySingleton.TL1 x 1
, Unbekannt x 1
37 - 44 bytes: Spring.Collections.Base.TInnerCollection<Spring.Container.Core.TComponentModel> x 1
, Spring.Collections.Base.TInnerCollection<System.Rtti.TValue> x 1
, Spring.Collections.Events.TCollectionChangedEventImpl<System.Rtti.TValue> x 1
, Spring.Collections.Events.TCollectionChangedEventImpl<Spring.Container.Core.TComponentModel> x 1
, Spring.Collections.Events.TCollectionChangedEventImpl<System.TObject> x 1
, Unbekannt x 1
45 - 52 bytes: Spring.Container.CreationContext.TCreationContext x 1
, System.SysUtils.TMultiReadExclusiveWriteSynchronizer x 1
, Spring.Collections.Stacks.TFoldedStack<System.TObject> x 1
, Spring.Collections.Lists.TList<System.Rtti.TValue> x 1
, Spring.Collections.Lists.TList<Spring.TNamedValue> x 1
, Spring.Collections.Lists.TList<Spring.TTypedValue> x 1
61 - 68 bytes: Spring.Container.Resolvers.@TLazyResolver.InternalResolve$ActRec<System.IInterface> x 1
69 - 76 bytes: System.SysUtils.TThreadLocalCounter x 1
117 - 124 bytes: Spring.Collections.Dictionaries.TDictionary<Spring.Container.Core.TComponentModel,System.Rtti.TValue> x 1
189 - 204 bytes: Unbekannt x 1

Comments (2)

  1. Stefan Glienke repo owner

    Thanks, that issue is known to me.

    Here is the reason why this currently happens: the factory attached to the lazy provided by the container has a reference to a creationcontext instance. That context keeps information during the resolve process which includes any singleton or perresolve instances from that resolve operation to avoid recreating them.

    That causes roughly this dependency graph: TL1 → Lazy<IL2> → factory → creationcontext → TL1 and thus we have a nice circular reference of strong references.

    The current design of the creationcontext and the way lazy resolutions cause it to live way beyond the initial resolve operation is unideal to say the least. The way singletons are currently handled by the container plays into this so a fix is not easy at this point.

    However Container refactoring is planned for the next big version after 2.0.

    Here is a workaround that you can use meanwhile: Implement Spring.Container.Common.IDisposable in TL1 and set FL2 to nil in the Dispose method. That is then being called on all singleton instance when the container is destroyed and thus breaks the circular dependencies.

  2. Log in to comment