You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:Microsoft.Extensions.DependencyInjection引发内存泄漏的原因分析

.NET 6 DI导致内存泄漏的原因及解决方法

泄漏原因分析

你的内存泄漏完全是因为根ServiceProvider的不当使用:

  1. 你通过BuildServiceProvider()创建的是根服务提供者,默认情况下,根提供者会跟踪所有由它创建的IDisposable类型服务(包括你的MySqlServer DbContext)。
  2. 由于你的m_serviceProvider是静态成员,永远不会被释放,这些被跟踪的MySqlServer实例会一直被根提供者持有引用,无法被GC回收,随着GetDatabase()的频繁调用,内存会持续增长。
  3. 虽然你用using块包裹了db实例,但根提供者的跟踪机制会覆盖Dispose的效果——实例的内存不会被真正释放,因为根容器还握着它的引用。

解决方法

1. 使用服务作用域(Service Scope)管理短生命周期服务

对于DbContext这类需要频繁创建且短生命周期的服务,必须通过作用域获取,作用域被释放时会自动清理内部的所有服务实例:

修改你的静态DI类,添加创建作用域的方法:

public static class DependencyInjection
{
    private static ServiceProvider m_serviceProvider = null;

    public static void Init(Func<IDatabase> createDatabaseMethod)
    {
        var serviceCollection = new ServiceCollection();
        // 注意:EF Core DbContext推荐注册为Scoped而非Transient
        serviceCollection.AddScoped<IDatabase, MySqlServer>();
        m_serviceProvider = serviceCollection.BuildServiceProvider();
    }

    public static IServiceScope CreateScope()
    {
        return m_serviceProvider.CreateScope();
    }
}

调用方式修改为:

using var scope = DependencyInjection.CreateScope();
using var db = scope.ServiceProvider.GetRequiredService<IDatabase>();
foreach (var appSettingPair in db.GetAppSettingsInDatabase())
    dictSettings.Add(appSettingPair.Key, appSettingPair.Value);

2. 遵循EF Core DbContext的生命周期最佳实践

DbContext本身设计为短生命周期,官方推荐将其注册为Scoped服务,而不是Transient。Scoped生命周期会确保每个作用域内只有一个DbContext实例,既避免重复创建的开销,又能正确被回收。

3. 避免手动管理静态ServiceProvider(推荐)

在.NET 6+中,应用的DI容器应该由框架托管(比如Program.cs中的WebApplicationBuilder.Services),通过构造函数注入获取服务,而不是自己写静态DI类。框架会自动处理作用域和服务的释放,从根源避免这类内存泄漏问题。

为什么移除DI后泄漏停止?

当你不使用DI直接创建MySqlServer实例时,using块会直接调用实例的Dispose方法,且没有任何外部容器持有它的引用,GC可以正常回收实例内存,因此不会出现泄漏。

内容的提问来源于stack exchange,提问作者AussieDude

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 11:01:24