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

检查与清理.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 20:15:00