.NET8 ASP.NET MemoryCache单服务器内存异常增长(23GB)排查问询
问题描述
我有一个基于.NET8的ASP.NET应用,每分钟接收100次REST调用,每次传输1MB数据,数据经JSON序列化后存入Microsoft.Extensions.Caching.Memory.MemoryCache。硬件配置相同的多数服务器内存占用约300MB,但某台Windows Server 2019服务器内存占用持续增长,最终达到系统上限23GB且从未释放。
我清楚在内存中存储这类大对象并非最优方案,但不允许使用外部缓存服务。我已在部分代码中通过MessagePack或Newtonsoft来销毁对象引用,具体实现代码如下:
public static T? DestroyReference<T>(T data) { return MessagePackSerializer.Deserialize<T?>( MessagePackSerializer.Serialize(data, MessagePackOptions), MessagePackOptions); }
请问哪些系统/配置因素会导致单台服务器内存过度增长而其他服务器稳定?有什么可行的排查思路?
我怀疑问题与大对象堆(Large Object Heap, LOH)碎片及垃圾回收(Garbage Collection, GC)相关,已考虑的可能原因包括:
- LOH碎片:频繁创建1MB对象引发LOH相关问题
- GC配置:Server GC与Workstation GC的差异或配置不同
- 缓存配置:MemoryCache旧条目未被清理(已限制条目数为300,但内存仍持续增长)
- 系统/环境差异:Windows设置存在差异(如内存压缩、页面文件、后台进程)
可能的系统/配置因素
1. GC配置差异
- 该服务器可能未启用Server GC(ASP.NET在服务器环境下默认启用,但可能被配置覆盖),Workstation GC针对单线程优化,高并发大对象场景下回收效率更低,易导致内存堆积。
- GC堆参数配置不同:比如
GCHeapCount、GCLargeObjectHeapCompactionMode,若未开启LOH压缩,碎片会持续累积无法回收。
2. Windows系统级设置差异
- 内存压缩:Windows Server 2019内存压缩功能若禁用,LOH碎片无法通过内存压缩缓解,物理内存占用持续上升。
- 页面文件配置:页面文件大小不足或禁用,GC无法将不常用内存页换出到磁盘,迫使内存持续占用。
- 后台进程干扰:该服务器运行高内存占用的后台进程或系统服务(如Windows更新、杀毒扫描),抢占内存导致GC无法触发有效回收。
3. MemoryCache配置细节差异
- 缓存过期策略未生效:虽限制条目数为300,但未配置绝对/滑动过期,或依赖项未触发失效,导致旧条目长期留存。
- 未配置内存大小限制:仅限制条目数但未设置
SizeLimit,若存在外部强引用持有缓存条目,实际内存占用会远超300MB预期。 - 未启用缓存压缩:.NET 8的
MemoryCache支持压缩,若该服务器未开启,大对象直接占用内存,而其他服务器启用后内存占用更低。
4. 运行时与应用池差异
-.NET运行时版本不一致:该服务器的.NET补丁版本与其他服务器不同,部分版本存在LOH回收逻辑bug,导致大对象无法有效回收。
- 应用池回收配置不同:该服务器应用池未配置内存阈值回收,或回收间隔过长,累积内存无法释放。
排查思路
1. 验证GC配置
- 检查应用配置文件(
appsettings.json/web.config),确认是否显式启用Server GC:.NET Core+查看"GCServer": true,传统.NET查看<gcServer enabled="true"/>。 - 使用
dotnet-counters实时监控GC指标:执行dotnet counters monitor --process-id <PID> System.Runtime,对比正常服务器的GC Heap Size、LOH Size、Gen 2 Collections数据。 - 检查LOH压缩配置:在应用启动代码中确认
GCLargeObjectHeapCompactionMode是否设置为CompactOnce或CompactAlways(.NET Core 3.0+支持)。
2. 检查Windows系统设置
- 验证内存压缩状态:任务管理器→性能→内存查看“内存压缩”状态,或执行PowerShell命令
Get-MMAgent | Select-Object MemoryCompression。 - 检查页面文件:右键此电脑→属性→高级系统设置→性能→设置→高级→虚拟内存,确认页面文件为系统管理大小且未禁用。
- 排查后台进程:任务管理器→详细信息按内存排序,找出异常占用内存的第三方进程或系统服务。
3. 分析MemoryCache行为
- 启用缓存日志:在
appsettings.json中添加日志配置,跟踪缓存条目添加、移除、过期事件,确认是否有条目未正常移除。 - 内存快照分析:使用
dotnet-dump或Visual Studio捕获内存快照,查看MemoryCache实际条目数量、单条大小,以及是否存在外部强引用导致条目无法回收。 - 验证大小限制配置:确认是否设置
MemoryCacheOptions.SizeLimit,并正确实现ICacheEntry.Size计算(默认未启用大小限制)。
4. 诊断LOH与内存泄漏
- 使用
dotnet-dump analyze:执行dumpheap -stat查看大对象分布,对比正常服务器;执行dumpheap -type <大对象类型>追踪引用链。 - 强制GC测试:调用
GC.GetTotalMemory(true)触发GC,观察内存是否下降。若内存未降,说明存在强引用泄漏;若下降后快速回升,说明LOH碎片严重。 - 对比.NET版本:执行
dotnet --version,确认该服务器与正常服务器的版本(含补丁)一致,不一致则升级到相同版本。
5. 检查应用池配置
- 查看IIS应用池回收设置:右键应用池→高级设置,检查“内存限制”“虚拟内存限制”及“固定时间间隔”,对比正常服务器配置。
- 排除32位模式:确认应用池未启用“启用32位应用程序”(虽23GB占用可能性低,但需排除)。
内容的提问来源于stack exchange,提问作者user25410615
相关产品推荐
相关产品推荐

