I was trying to debug a memory leak in my app (see related question) and have encountered a smilingly wrong behavior.
In this code (simplified snippet, of course):
while (true)
{
using (var context = _serviceProvider.GetRequiredService<IDataContext>())
{
Console.WriteLine("Hello");
}
}
Memory consumption grows rapidly.
If I comment out service spawn, memory consumption is stable.
while (true)
{
// using (var context = _serviceProvider.GetRequiredService<IDataContext>())
// {
Console.WriteLine("Hello");
// }
}
Service was registered as transient.
My understanding is that using
statement is responsible for disposing a service. var context
is created in the scope of while
and should be destroyed when new iteration begins.
My first thought was that GC just does not do its job frequently enough, but does not frequency increase when amount of consumed memory increases?
Why am I wrong?
These classes need to dispose of these objects. I created a virtual Dispose method in the base ScreenObject base class and then implemented an override Dispose method in each of the derived classes that hold onto unmanaged resources.
Dispose() will not be called automatically. If there is a finalizer it will be called automatically. Implementing IDisposable provides a way for users of your class to release resources early, instead of waiting for the garbage collector.
When the close brace is reached, the Dispose( ) method will be called on the object automatically, as illustrated in Example 4-6. In the first part of this example, the Font object is created within the using statement. When the using statement ends, Dispose( ) is called on the Font object.
Calling Dispose on the class will cause it to notify the file system that it no longer needs the file, and it may thus be made available to other entities. In general, if an object which implements IDisposable is abandoned without calling Dispose , some things which should get done, won't be. There is a mechanism in .
After days of fighting the problem, I finally derived the answer. In short, the issue is that Microsoft DI container does not dispose transient services, it keeps references on them.
Here is the corresponding issue on github.
The developers do not plan to fix it since the cost (complexity and hackiness) of fixing overweighs the benefits.
Suggested workaround is using scoped service instead of the transient one.
Here is an example code, see more in issue.
using (var scope = serviceProvider.CreateScope())
using (var context = scope.ServiceProvider.GetRequiredService<IDataContext>())
{ ... }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With