MemoryCache在配置中不遵守内存限制

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (430)

我正在使用应用程序中的.NET 4.0 MemoryCache类,并尝试限制最大缓存大小,但在我的测试中,似乎缓存实际上并未遵守限制。

根据MSDN,我正在使用这些设置来限制缓存大小:

  1. CacheMemoryLimitMegabytes:对象实例可以增长到的最大内存大小(以兆字节为单位)。“
  2. PhysicalMemoryLimitPercentage “高速缓存可使用的物理内存百分比,表示为1到100之间的整数值。默认值为零,表示 MemoryCache实例根据安装在内存中的内存量管理其自己的内存 1电脑。” 1.这不完全正确 - 低于4的任何值都会被忽略并替换为4。

我知道这些值是近似的,并不是硬限制,因为清除缓存的线程每隔x秒触发一次,并且还取决于轮询间隔和其他未公开的变量。但是,即使考虑到这些差异,在将CacheMemoryLimitMegabytesPhysicalMemoryLimitPercentage设置在一起或在测试应用程序中单独设置时,第一个项目将从缓存中逐出时,我会看到极其不一致的缓存大小。为了确保我运行了每次测试10次并计算出平均值。

这些是在具有3GB RAM的32位Windows 7 PC上测试以下示例代码的结果。缓存大小是在每次测试首次调用CacheItemRemoved()之后进行的。(我知道缓存的实际大小会比这大)

MemLimitMB    MemLimitPct     AVG Cache MB on first expiry    
   1            NA              84
   2            NA              84
   3            NA              84
   6            NA              84
  NA             1              84
  NA             4              84
  NA            10              84
  10            20              81
  10            30              81
  10            39              82
  10            40              79
  10            49              146
  10            50              152
  10            60              212
  10            70              332
  10            80              429
  10           100              535
 100            39              81
 500            39              79
 900            39              83
1900            39              84
 900            41              81
 900            46              84

 900            49              1.8 GB approx. in task manager no mem errros
 200            49              156
 100            49              153
2000            60              214
   5            60              78
   6            60              76
   7           100              82
  10           100              541

这是测试应用程序:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Runtime.Caching;
using System.Text;
namespace FinalCacheTest
{       
    internal class Cache
    {
        private Object Statlock = new object();
        private int ItemCount;
        private long size;
        private MemoryCache MemCache;
        private CacheItemPolicy CIPOL = new CacheItemPolicy();

        public Cache(long CacheSize)
        {
            CIPOL.RemovedCallback = new CacheEntryRemovedCallback(CacheItemRemoved);
            NameValueCollection CacheSettings = new NameValueCollection(3);
            CacheSettings.Add("CacheMemoryLimitMegabytes", Convert.ToString(CacheSize)); 
            CacheSettings.Add("physicalMemoryLimitPercentage", Convert.ToString(49));  //set % here
            CacheSettings.Add("pollingInterval", Convert.ToString("00:00:10"));
            MemCache = new MemoryCache("TestCache", CacheSettings);
        }

        public void AddItem(string Name, string Value)
        {
            CacheItem CI = new CacheItem(Name, Value);
            MemCache.Add(CI, CIPOL);

            lock (Statlock)
            {
                ItemCount++;
                size = size + (Name.Length + Value.Length * 2);
            }

        }

        public void CacheItemRemoved(CacheEntryRemovedArguments Args)
        {
            Console.WriteLine("Cache contains {0} items. Size is {1} bytes", ItemCount, size);

            lock (Statlock)
            {
                ItemCount--;
                size = size - 108;
            }

            Console.ReadKey();
        }
    }
}

namespace FinalCacheTest
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            int MaxAdds = 5000000;
            Cache MyCache = new Cache(1); // set CacheMemoryLimitMegabytes

            for (int i = 0; i < MaxAdds; i++)
            {
                MyCache.AddItem(Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
            }

            Console.WriteLine("Finished Adding Items to Cache");
        }
    }
}

为什么MemoryCache不遵守配置的内存限制?

提问于
用户回答回答于

正在读取设置,但CLR本身似乎存在深层次的问题,看起来会使内存限制设置基本无用。

下面的代码从System.Runtime.Caching DLL中反映出来,用于CacheMemoryMonitor类(有一个类似的类监视物理内存并处理另一个设置,但这是更重要的一个):

protected override int GetCurrentPressure()
{
  int num = GC.CollectionCount(2);
  SRef ref2 = this._sizedRef;
  if ((num != this._gen2Count) && (ref2 != null))
  {
    this._gen2Count = num;
    this._idx ^= 1;
    this._cacheSizeSampleTimes[this._idx] = DateTime.UtcNow;
    this._cacheSizeSamples[this._idx] = ref2.ApproximateSize;
    IMemoryCacheManager manager = s_memoryCacheManager;
    if (manager != null)
    {
      manager.UpdateCacheSize(this._cacheSizeSamples[this._idx], this._memoryCache);
    }
  }
  if (this._memoryLimit <= 0L)
  {
    return 0;
  }
  long num2 = this._cacheSizeSamples[this._idx];
  if (num2 > this._memoryLimit)
  {
    num2 = this._memoryLimit;
  }
  return (int) ((num2 * 100L) / this._memoryLimit);
}

您可能会注意到的第一件事是,它甚至在Gen2垃圾回收之后才会尝试查看缓存的大小,而不是仅仅回退cacheSizeSamples中的现有存储大小值。所以你永远不可能直接击中目标,但是如果剩下的工作起来,我们至少可以在我们遇到麻烦之前进行尺寸测量。

因此,假设发生了Gen2 GC,我们遇到了问题2,即ref2.ApproximateSize在真正接近缓存大小方面做了一件糟糕的工作。通过CLR垃圾进行阻塞我发现这是一个System.SizedReference,这是它获取值的过程(IntPtr是MemoryCache对象本身的句柄):

[SecurityCritical]
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern long GetApproximateSizeOfSizedRef(IntPtr h);

所属标签

可能回答问题的人

  • Hanzo

    6 粉丝0 提问7 回答
  • Richel

    9 粉丝0 提问3 回答
  • mariolu

    31 粉丝0 提问2 回答
  • 上云小秘书

    15 粉丝0 提问2 回答

扫码关注云+社区

领取腾讯云代金券