title: MemoryCache does not obey memory limits in configuration
body: <p>I’m working with the .NET 4.0 <a href="http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache(v=vs.110).aspx" rel="noreferrer"><strong>MemoryCache</strong></a> class in an application and trying to limit the maximum cache size, but in my tests it does not appear that the cache is actually obeying the limits. </p>
  
  <p>I'm using the settings which, <a href="http://msdn.microsoft.com/en-us/library/system.runtime.caching.configuration.memorycacheelement(v=vs.110).aspx" rel="noreferrer" title="MSDN: MemoryCacheElement">according to MSDN</a>, are supposed to limit the cache size:</p>
  
  <blockquote>
    <ol>
    <li><a href="http://msdn.microsoft.com/en-us/library/system.runtime.caching.configuration.memorycacheelement.cachememorylimitmegabytes(v=vs.110).aspx" rel="noreferrer"><strong>CacheMemoryLimitMegabytes</strong></a>: The maximum memory size, in megabytes, that an instance of an object can grow to."</li>
    <li><strong><a href="http://msdn.microsoft.com/en-us/library/system.runtime.caching.configuration.memorycacheelement.physicalmemorylimitpercentage(v=vs.110).aspx" rel="noreferrer">PhysicalMemoryLimitPercentage</a>:</strong> "The percentage of physical memory that the cache can use, expressed as an integer value from 1 to 100. The default is zero, which indicates that <strong>MemoryCache</strong> instances manage their own memory<sup><b>1</b></sup> based on the amount of memory that is installed on the computer." <sup><b>1.</b> This is not entirely correct-- any value below 4 is ignored and replaced with 4.</sup></li>
    </ol>
  </blockquote>
  
  <p>I understand that these values are approximate and not hard limits as the thread that purges the cache is fired every x seconds and is also dependent on the polling interval and other undocumented variables. However even taking into account these variances, I'm seeing wildly inconsistent cache sizes when the first item is being evicted from the cache after setting <strong>CacheMemoryLimitMegabytes</strong>  and <strong>PhysicalMemoryLimitPercentage</strong> together or singularly  in a test app.  To be sure I ran each test 10 times and calculated the average figure.</p>
  
  <p>These are the results of testing the example code below on a 32-bit Windows 7 PC with 3GB of RAM. Size of the cache is taken after the first call to <strong>CacheItemRemoved()</strong> on each test. (I am aware the actual size of cache will be larger than this)</p>
  
  <pre class="lang-cs prettyprint-override"><code>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
  </code></pre>
  
  <p>Here is the test application:</p>
  
  <pre class="lang-cs prettyprint-override"><code>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 &lt; MaxAdds; i++)
              {
                  MyCache.AddItem(Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
              }
  
              Console.WriteLine("Finished Adding Items to Cache");
          }
      }
  }
  </code></pre>
  
  <p>Why is <strong>MemoryCache</strong> not obeying the configured memory limits?</p>