求助:Microsoft.Extensions.DependencyInjection引发内存泄漏的原因分析
.NET 6 DI导致内存泄漏的原因及解决方法
泄漏原因分析
你的内存泄漏完全是因为根ServiceProvider的不当使用:
- 你通过
BuildServiceProvider()创建的是根服务提供者,默认情况下,根提供者会跟踪所有由它创建的IDisposable类型服务(包括你的MySqlServerDbContext)。 - 由于你的
m_serviceProvider是静态成员,永远不会被释放,这些被跟踪的MySqlServer实例会一直被根提供者持有引用,无法被GC回收,随着GetDatabase()的频繁调用,内存会持续增长。 - 虽然你用
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
相关产品推荐
相关产品推荐

