C# 在ASP.NET中使用System.Runtime.Caching.MemoryCache会消耗大量内存

C# 在ASP.NET中使用System.Runtime.Caching.MemoryCache会消耗大量内存,c#,asp.net-mvc-4,.net-4.0,C#,Asp.net Mvc 4,.net 4.0,我正在asp.net应用程序中使用MemoryCache。在我的应用程序运行数小时后,w3wp进程会消耗大量内存,可以达到10GB的内存,之后应用程序会变得无响应,IIS会自动重置它(通过ping机制) 我分析了应用程序流程,发现MemoryCache在内部创建了MemoryCacheStore的多个实例(相当于CPU的数量),每个实例都会消耗大量内存 似乎出于某种原因,MemoryCache在多个实例中存储相同的数据,这会导致巨大的内存消耗 如下图所示,我有16个CPU,16个MemoryCa

我正在asp.net应用程序中使用MemoryCache。在我的应用程序运行数小时后,w3wp进程会消耗大量内存,可以达到10GB的内存,之后应用程序会变得无响应,IIS会自动重置它(通过ping机制)

我分析了应用程序流程,发现MemoryCache在内部创建了MemoryCacheStore的多个实例(相当于CPU的数量),每个实例都会消耗大量内存

似乎出于某种原因,MemoryCache在多个实例中存储相同的数据,这会导致巨大的内存消耗

如下图所示,我有16个CPU,16个MemoryCacheStore是由单个MemoryCache实例在内部创建的。我检查了MemoryCache的源代码,发现它创建了一个环境MemoryCacheStore数组。处理器很重要,但没有发现为什么所有这些实例都使用内存,以及如何防止这种情况?


这就是内存缓存的工作——在内存中缓存数据。如果您使用了错误的键,或者将过期设置为无限,您将在内存中写入每条记录,永远不会重用任何内容,永远不会删除过时的数据。发布向
MemoryCache
添加项的代码、使用它的代码、缓存设置,特别是过期和内存限制。这里发布的信息没有用-它描述了任何类型的缓存应该做什么。MemoryCache可以配置为使用最大百分比的RAM,或者通过。解释默认值,例如x64系统的最小60%RAM或1TB。可能的重复也指出了问题的可能原因-使用默认限制创建太多MemoryCache实例。如果代码创建了12个MemoryCache实例,每个实例可以占用60%的RAM,那么它们的内存就会耗尽RAM@PanagiotisKanavos我已经添加了MemoryCacheManager类,并添加了如何使用它。请注意,我为我的应用程序创建了一个MemoryCacheManager实例,这就是内存缓存的工作—在内存中缓存数据。如果您使用了错误的键,或者将过期设置为无限,您将在内存中写入每条记录,永远不会重用任何内容,永远不会删除过时的数据。发布向
MemoryCache
添加项的代码、使用它的代码、缓存设置,特别是过期和内存限制。这里发布的信息没有用-它描述了任何类型的缓存应该做什么。MemoryCache可以配置为使用最大百分比的RAM,或者通过。解释默认值,例如x64系统的最小60%RAM或1TB。可能的重复也指出了问题的可能原因-使用默认限制创建太多MemoryCache实例。如果代码创建了12个MemoryCache实例,每个实例可以占用60%的RAM,那么它们的内存就会耗尽RAM@PanagiotisKanavos我已经添加了MemoryCacheManager类,并添加了如何使用它。请注意,我为我的应用程序创建了一个MemoryCacheManager实例
    public partial class MemoryCacheManager : DisposableObject, ICacheManager
{
    const string LockRecursionExceptionMessage = "Acquiring identical cache items recursively is not supported. Key: {0}";

    // Wwe put a special string into cache if value is null,
    // otherwise our 'Contains()' would always return false,
    // which is bad if we intentionally wanted to save NULL values.
    public const string FakeNull = "__[NULL]__";

    private readonly Work<ICacheScopeAccessor> _scopeAccessor;
    private MemoryCache _cache;
    private readonly ConcurrentDictionary<string, SemaphoreSlim> _keyLocks = new ConcurrentDictionary<string, SemaphoreSlim>();

    public MemoryCacheManager(Work<ICacheScopeAccessor> scopeAccessor)
    {
        _scopeAccessor = scopeAccessor;
        _cache = CreateCache();

    }

    private MemoryCache CreateCache()
    {
        return new MemoryCache("SmartStore" + DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss"));
    }

    public bool IsDistributedCache
    {
        get { return false; }
    }

    private bool TryGet<T>(string key, bool independent, out T value)
    {
        value = default(T);

        object obj = _cache.Get(key);

        if (obj != null)
        {
            // Make the parent scope's entry depend on this
            if (!independent)
            {
                _scopeAccessor.Value.PropagateKey(key);
            }

            if (obj.Equals(FakeNull))
            {
                return true;
            }

            value = (T)obj;
            return true;
        }

        return false;
    }

    public T Get<T>(string key, bool independent = false)
    {
        TryGet(key, independent, out T value);
        return value;
    }

    public T Get<T>(string key, Func<T> acquirer, TimeSpan? duration = null, bool independent = false)
    {
        if (TryGet(key, independent, out T value))
        {
            return value;
        }

        if (_scopeAccessor.Value.HasScope(key))
        {
            throw new LockRecursionException(LockRecursionExceptionMessage.FormatInvariant(key));
        }

        // Get the (semaphore) locker specific to this key
        using (KeyedLock.Lock("cache:" + key, TimeSpan.FromMinutes(1)))
        {
            // Atomic operation must be outer locked
            if (!TryGet(key, independent, out value))
            {
                using (_scopeAccessor.Value.BeginScope(key))
                {
                    value = acquirer();
                    Put(key, value, duration, _scopeAccessor.Value.Current.Dependencies);
                    return value;
                }
            }
        }

        return value;
    }

    public async Task<T> GetAsync<T>(string key, Func<Task<T>> acquirer, TimeSpan? duration = null, bool independent = false)
    {
        if (TryGet(key, independent, out T value))
        {
            return value;
        }

        if (_scopeAccessor.Value.HasScope(key))
        {
            throw new LockRecursionException(LockRecursionExceptionMessage.FormatInvariant(key));
        }

        // Get the async (semaphore) locker specific to this key
        using (await KeyedLock.LockAsync("cache:" + key, TimeSpan.FromMinutes(1)))
        {
            if (!TryGet(key, independent, out value))
            {
                using (_scopeAccessor.Value.BeginScope(key))
                {
                    value = await acquirer();
                    Put(key, value, duration, _scopeAccessor.Value.Current.Dependencies);
                    return value;
                }
            }
        }

        return value;
    }

    public void Put(string key, object value, TimeSpan? duration = null, IEnumerable<string> dependencies = null)
    {
        _cache.Set(key, value ?? FakeNull, GetCacheItemPolicy(duration, dependencies));
    }

    public bool Contains(string key)
    {
        return _cache.Contains(key);
    }

    public void Remove(string key)
    {
        _cache.Remove(key);
    }

    public IEnumerable<string> Keys(string pattern)
    {
        Guard.NotEmpty(pattern, nameof(pattern));

        var keys = _cache.AsParallel().Select(x => x.Key);

        if (pattern.IsEmpty() || pattern == "*")
        {
            return keys.ToArray();
        }

        var wildcard = new Wildcard(pattern, RegexOptions.IgnoreCase);
        return keys.Where(x => wildcard.IsMatch(x)).ToArray();
    }

    public int RemoveByPattern(string pattern)
    {
        lock (_cache)
        {
            var keysToRemove = Keys(pattern);
            int count = 0;

            // lock atomic operation
            foreach (string key in keysToRemove)
            {
                _cache.Remove(key);
                count++;
            }

            return count;
        }
    }

    public void Clear()
    {
        // Faster way of clearing cache: https://stackoverflow.com/questions/8043381/how-do-i-clear-a-system-runtime-caching-memorycache
        var oldCache = Interlocked.Exchange(ref _cache, CreateCache());
        oldCache.Dispose();
        GC.Collect();
    }

    public virtual ISet GetHashSet(string key, Func<IEnumerable<string>> acquirer = null)
    {
        var result = Get(key, () =>
        {
            var set = new MemorySet(this);
            var items = acquirer?.Invoke();
            if (items != null)
            {
                set.AddRange(items);
            }

            return set;
        });

        return result;
    }

    private CacheItemPolicy GetCacheItemPolicy(TimeSpan? duration, IEnumerable<string> dependencies)
    {
        var absoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration;

        if (duration.HasValue)
        {
            absoluteExpiration = DateTime.UtcNow + duration.Value;
        }

        var cacheItemPolicy = new CacheItemPolicy
        {
            AbsoluteExpiration = absoluteExpiration,
            SlidingExpiration = ObjectCache.NoSlidingExpiration
        };

        if (dependencies != null && dependencies.Any())
        {
            // INFO: we can only depend on existing items, otherwise this entry will be removed immediately.
            dependencies = dependencies.Where(x => x != null && _cache.Contains(x));
            if (dependencies.Any())
            {
                cacheItemPolicy.ChangeMonitors.Add(_cache.CreateCacheEntryChangeMonitor(dependencies));
            }
        }

        //cacheItemPolicy.RemovedCallback = OnRemoveEntry;

        return cacheItemPolicy;
    }

    //private void OnRemoveEntry(CacheEntryRemovedArguments args)
    //{
    //  if (args.RemovedReason == CacheEntryRemovedReason.ChangeMonitorChanged)
    //  {
    //      Debug.WriteLine("MEMCACHE: remove depending entry '{0}'.".FormatInvariant(args.CacheItem.Key));
    //  }
    //}

    protected override void OnDispose(bool disposing)
    {
        if (disposing)
            _cache.Dispose();
    }
}
_requestCache.Get(key, () =>
        {
            var query = _categoryRepository.Table;

            if (!showHidden)
                query = query.Where(c => c.Published);

            query = query.Where(c => c.ParentCategoryId == parentCategoryId && !c.Deleted);

            query = ApplyHiddenCategoriesFilter(query, storeId, showHidden);

            var categories = query.OrderBy(x => x.DisplayOrder).ToList();
            return categories;
        });