IServiceProvider垃圾收集/处置

时间:2017-04-06 01:45:16

标签: c# memory-leaks .net-core

我试图在我的应用中调试内存泄漏(请参阅相关的question)并遇到了一个微笑错误的行为。

在此代码中(当然是简化的代码段):

    while (true)
    {
        using (var context = _serviceProvider.GetRequiredService<IDataContext>())
        {
            Console.WriteLine("Hello");
        }
    }

内存消耗迅速

如果我注释掉服务spawn,内存消耗是稳定的。

    while (true)
    {
        // using (var context = _serviceProvider.GetRequiredService<IDataContext>())
        // {
            Console.WriteLine("Hello");
        // }
    }

服务已注册为 transient

我的理解是using语句负责处理服务。 var context是在while的范围内创建的,并且应该在新的迭代开始时销毁。

我的第一个想法是GC只是不经常做它的工作,但是当消耗的内存量增加时频率没有增加?

为什么我错了?

2 个答案:

答案 0 :(得分:4)

经过几天的斗争,我终于得出了答案。简而言之,问题是Microsoft DI容器不会处置瞬态服务,它会保留对它们的引用。

这是github上相应的issue

开发人员不打算修复它,因为修复的成本(复杂性和黑客性)超过了好处。

建议的解决方法是使用作用域服务而不是瞬态服务。

以下是示例代码,请参阅issue中的详细信息。

using (var scope = serviceProvider.CreateScope())
using (var context = scope.ServiceProvider.GetRequiredService<IDataContext>())
{ ... }

答案 1 :(得分:1)

仅需指出,从瞬态服务切换到作用域服务可能还不够。在我的应用程序中甚至根本不需要。

我的问题是,正如您在回答中已经说过的那样, ServiceProvider会跟踪它创建的所有对象。 长时间运行的应用程序带有从未处置过的容器会导致巨大的内存消耗。即使容器仅创建瞬态对象(甚至不是IDisposable的对象),它们也会迅速“超出范围”运行。由于ServiceProvider仍在跟踪这些对象,因此GC无法完成其工作。

请考虑使用建议使用范围限定的ServiceProvider的事实,因为处置它会同时终止其所有子项的生命(作用域和瞬态)。 不要让容器(儿童)越界越长。甚至对临时对象也要使用范围限定的ServiceProvider。