aspnet / DependencyInjection

[Archived] Contains common DI abstractions that ASP.NET Core and Entity Framework Core use. Project moved to https://github.com/aspnet/Extensions

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Make ServiceScope track list of IDisposable as WeakReference

nh43de opened this issue · comments

Problem: memory leak caused by references to disposed objects in service scope
Summary: We have found that the DI container service scope will hold onto objects that have been disposed, preventing them from being garbage collected.
Location: DependencyInjection/src/DI/ServiceLookup/ServiceProviderEngineScope.cs
Field: _disposables
Root Cause: DI container reference to disposed objects prevents them from being garbage collected
Proposed Solution: replace List to IWeakCollection - collection of weak references. I'm not sure if this is compatible with the design intentions of MS DI.

In ServiceProviderEngineScope.cs, we are tracking a List to track objects that will be disposed when the service scope is disposed. However, this can cause objects that have already been disposed to be retained by the garbage collector, even if the only reference to the object is in the DI container service scope.

In our fork of the project, we have replaced the List with Stephen Cleary's IWeakCollection, and have found that the memory leak goes away. Also, all unit tests pass. This way, the DI container's internal reference to the object does not make the GC consider them as rooted.

http://nitokitchensink.codeplex.com/sourcecontrol/changeset/view/27265#453683

This sounds completely opposite of how scopes are intended to be used. A scope is meant to have a short lifetime (typically a request lifetime). The tracked disposables are owned by the container and are supposed to be diposed by the container.

The solution here would be to register the services as transient and use the container for activation, but take ownership and track instances for disposal yourself. So if you're talking about transient instances, I completely agree with you. In fact, I don't even think transients should be tracked at all (see #456). Unfortunately, the linked issue has been closed, so I wouldn't hold my breath for a fix for these memory leaks in the near future...

I was talking about transient services. If we are going to track them, making them weak references allows for GC to collect them when they are no longer referenced and have been disposed.

@nh43de It is the responsibility of the Scope to dispose disposable instances. For it to be able to do this, it requires a (hard) reference to such object. Changing the reference to a weak reference might cause the Scope to lose the reference to it, which makes it impossible for it to call Dispose on such object. This by itself could cause problems such as connection pool timeouts and out of memory problems, because of the undeterministic nature of the GC concerning the invocation of Finalizer methods that need to be called in the absense of a call to Dispose().

@dotnetjunkie Thanks for the enlightening comments and I really appreciate you taking the time to respond. This scenario definitely makes sense, and something probably best avoided in favor of proper scope handling.

This issue was moved to dotnet/aspnetcore#2336