检查与清理.NET MemoryCache,解决w3wp.exe内存持续上涨问题
问题根源与解决方案
1. 代码层面的直接问题
你定义的类级别私有字段m_Companies_All是内存不释放的核心原因:只要当前CustomerConfig类的实例没有被回收,这个字段就会一直持有加载的_CompanyCollection集合强引用。哪怕你从MemoryCache中删除了对应键、缓存条目被销毁,这个字段的引用仍然存在,GC会判定对象仍在使用,完全不会回收这块内存。
如果你的CustomerConfig是业务常用的单例模式,这个字段的引用会一直存在到应用池回收,相当于同时在缓存和类字段中存储了两份相同集合,内存占用直接翻倍。
2. 修复代码示例
直接删除类级别m_Companies_All字段的定义,改用局部变量接收缓存值,不要持有全局引用:
//<summary>获取当前CustomerConfig下所有可用的活跃公司</summary> Public Property Companies_All As Core._CompanyCollection Implements ICustomerConfig.Companies_All Get Dim xCacheKey As String = Me.CacheKey("Core.Companies_All") Dim xCacheItem As Runtime.Caching.CacheItem = Runtime.Caching.MemoryCache.Default.GetCacheItem(xCacheKey) Dim result As Core._CompanyCollection If xCacheItem Is Nothing Then result = Core._CompanyCollection.All(Core._Company.eStates.Active, Me) xCacheItem = New Runtime.Caching.CacheItem(xCacheKey, result) ' 缓存有效期20分钟 Dim xCachePolicy As New Runtime.Caching.CacheItemPolicy With {.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(20)} '插入缓存 Runtime.Caching.MemoryCache.Default.Add(xCacheItem, xCachePolicy) Else result = DirectCast(xCacheItem.Value, Core._CompanyCollection) End If Return result End Get Set(value As Core._CompanyCollection) ' 主动更新缓存,不要存储类字段 Dim xCacheKey As String = Me.CacheKey("Core.Companies_All") Runtime.Caching.MemoryCache.Default.Set(xCacheKey, value, DateTimeOffset.UtcNow.AddMinutes(20)) End Set End Property
3. 内存泄漏验证方法
- 抓进程内存转储:打开任务管理器找到w3wp.exe,右键选择「创建转储文件」,保存dmp文件后用Visual Studio打开,选择「调试托管内存」,搜索
_CompanyCollection类型,即可看到所有实例的数量、大小、引用链,如果引用根是CustomerConfig类的字段,就实锤是全局引用导致的无法回收。 - 你之前调用
GC.Collect()无效的原因就是存在未释放的强引用,不是GC不工作,是对象还处于被引用状态,不会被标记为可回收。
4. MemoryCache内容查看方法
可以在测试环境加调试接口,遍历输出缓存的所有键、类型、估算大小:
Public Function GetCacheDiagnostics() As List(Of (CacheKey As String, ValueType As String, ApproximateSize As Long)) Dim result As New List(Of (String, String, Long)) ' 遍历MemoryCache所有条目 For Each entry In Runtime.Caching.MemoryCache.Default Dim size As Long = -1 If entry.Value IsNot Nothing Then Try ' 二进制序列化估算对象大小,不可序列化对象会抛出异常 Dim formatter As New Runtime.Serialization.Formatters.Binary.BinaryFormatter() Using ms As New IO.MemoryStream() formatter.Serialize(ms, entry.Value) size = ms.Length End Using Catch End Try End If result.Add((entry.Key, entry.Value?.GetType().FullName, size)) Next Return result End Function
注意:遍历缓存会触发全局锁,禁止在生产环境调用此方法
5. 其他优化建议
- 配置MemoryCache内存上限:.NET Framework默认MemoryCache的回收阈值是物理内存的90%,如果服务器内存较大,会导致迟迟不触发自动清理,可以在web.config中修改配置:
<system.runtime.caching> <memoryCache> <namedCaches> <add name="Default" physicalMemoryLimitPercentage="20" pollingInterval="00:01:00" /> </namedCaches> </memoryCache> </system.runtime.caching>
以上配置会限制缓存最多占用20%物理内存,每分钟检查一次是否超限,自动清理过期条目。
- 解决Session丢失问题:将Session存储模式从InProc改为StateServer或SqlServer,就算应用池全量回收,用户登录态也不会丢失。
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

