.NET 8 IIS应用EF Core内存泄漏及OutOfMemoryException问题求助
.NET 8 IIS托管应用内存管理优化方案
GC配置调整(解决内存不归还操作系统问题)
- 确认启用服务器GC:IIS托管场景下服务器GC更适配多线程高负载,默认已启用,可通过配置明确指定:
// runtimeconfig.json { "runtimeOptions": { "configProperties": { "System.GC.Server": true, "System.GC.HeapCount": 0 // 自动适配CPU核数分配堆,避免内存分片过度 } } } - 开启大对象堆(LOH)压缩:LOH碎片会导致内存无法有效回收,强制GC后压缩堆内存:
// 在Program.cs启动逻辑中添加 GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnEachFullGC; - 让GC更主动归还内存给OS:默认GC会保留已申请的虚拟内存,调整配置触发更积极的内存释放:
注意:// runtimeconfig.json { "runtimeOptions": { "configProperties": { "System.GC.RetainVM": false, // 关闭虚拟内存保留,GC后主动归还内存给OS "System.GC.HardLimitPercent": 70 // 进程内存占比达70%时触发Full GC并尝试释放 } } }RetainVM=false可能增加GC频率,需结合压测验证性能影响。
EF Core只读场景优化
- 强制无跟踪查询:只读操作不需要实体跟踪,大幅减少内存开销,支持全局或单个查询配置:
// 单个查询配置 var speakers = await _context.Speakers.AsNoTracking().ToListAsync(); // 全局配置(Program.cs) builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Default")) .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)); - 避免一次性加载全量数据:如果查询结果集过大,用分页或流式查询降低内存占用:
// 分页查询 var pageSpeakers = await _context.Speakers.AsNoTracking() .Skip((pageIndex - 1) * pageSize) .Take(pageSize) .ToListAsync(); // 流式查询(适配超大数据集) await using var speakerStream = _context.Speakers.AsNoTracking().AsAsyncEnumerable(); await foreach (var speaker in speakerStream) { // 逐行处理数据 } - 排查DbContext泄漏:确认Scoped的DbContext没有被单例服务、静态变量意外持有,否则会导致上下文及关联实体无法被GC回收。
代码与内存泄漏排查
- 检查非托管资源释放:所有使用非托管资源(文件流、第三方原生库等)的代码必须通过
using语句或IDisposable接口正确释放:using var fileStream = new FileStream("data.txt", FileMode.Open); // 操作流逻辑 - 定位大对象分配:Gen0内存暴涨至GB级,通常是一次性分配了大量大对象(>85KB)。排查是否查询了超大文本字段、生成了巨型集合,或第三方库存在不合理的内存分配。
- 利用DotMemory分析快照:对比GC前后的内存分布,重点关注:
- 静态集合/缓存是否无限增长
- EF Core查询缓存大小(可通过
QueryCacheSize限制) - 未取消的事件订阅、委托引用导致的对象持有
IIS与应用池配置调整
- 配置应用池回收策略:设置定期回收(如每日凌晨)或基于内存阈值回收(如私有内存超过4GB时回收),避免单个应用内存持续累积。注意回收会导致短暂请求中断,需结合业务场景调整。
- 限制应用池内存上限:在IIS应用池高级设置中,设置“私有内存限制”和“虚拟内存限制”,进程超过阈值时自动回收,防止占用过多服务器资源。
- 优化应用池队列:调整“队列长度”和“最大工作进程数”,避免请求积压导致内存占用飙升。
测试应用验证步骤
针对你的极简测试应用,按以下步骤验证优化效果:
- 给EF查询添加
.AsNoTracking() - 配置
System.GC.RetainVM=false和服务器GC - 再次用DotMemory对比GC前后的内存释放情况,确认是否有明显改善
内容的提问来源于stack exchange,提问作者steryd
相关产品推荐
相关产品推荐

