5

I am trying to implement some caching in my application and i want to use the default memory cache in C# (this requirement can be changed if this it wont work). My problem is that don't want to exceed the maximum amount of physical memory i have on the machine, but as i understand i can't add such a constraint to the default memory cache.

In general the policy is:

  1. If the object has been in the cache for 10 min with no requests it is removed
  2. If a new object is added to the cache and the maximum amount of avaliable physical memory is close to used, elements are removed based on LRU

My cache can contain many different objects and they range from 10mb to 2-3gb, so i can't really get the trim function to work.

Are there any suggestions on how to implement a LRU cache monitoring the ram usage? And hoppefully it can be done using the caches in .net?

Edit

I've added a simple example where the MemoryCache is limited to 100Mb and 20% of the physical memory, but that does not change anything. My memory is filled with no removal of cache entries. Note that the polling interval is changed to evert 5 second.

class Item
{
    private List<Guid> data;

    public Item(int capacity)
    {
        this.data = new List<Guid>(capacity);
        for (var i = 0; i < capacity; i++)
            data.Add(Guid.NewGuid());
    }
}

class Program
{
    static void Main(string[] args)
    {
        var cache = new MemoryCache(
            "MyCache",
            new NameValueCollection
            {
                { "CacheMemoryLimitMegabytes", "100" },
                { "PhysicalMemoryLimitPercentage", "20" },
                { "PollingInterval", "00:00:05" }
            });

        for (var i = 0; i < 10000; i++)
        {
            var key = String.Format("key{0}", i);
            Console.WriteLine("Add item: {0}", key);
            cache.Set(key, new Item(1000000), new CacheItemPolicy() { UpdateCallback = UpdateHandler } );
        }

        Console.WriteLine("\nDone");
        Console.ReadKey();
    }

    public static void UpdateHandler(CacheEntryUpdateArguments args)
    {
        Console.WriteLine("Remove: {0}, Reason: {1}", args.Key, args.RemovedReason.ToString());
    }
}
aweis
  • 5,350
  • 4
  • 30
  • 46

1 Answers1

4

Looks like the System.Runtime.Caching.MemoryCache class would fit this bill nicely. You set caching policy on a per-item basis, so if you add an item with a cache policy of SlidingExpiration with a TimeSpan of 10min, you should get the behavior you are looking for.

This is a .Net v4 class only, so it doesn't exist on earlier runtime versions. If you are in a web context, the ASP.Net cache behaves similarly, but will probably not let you manage system information.

You can set limits on cache size when you create it so that it does not exceed a certain memory footprint:

var myCache = new MemoryCache( 
    "MyCache",
    new NameValueCollection { { "PhysicalMemoryLimit", "50" }} // set max mem pct
    );

This should prevent any paging to disk, at least within your application. If there are outside memory pressures or the system is overly aggressive about paging memory to disk, your cache may still be paged out, but not due to overuse within your application. To my knowledge there is no way to reserve the memory pages in C#.

saarp
  • 1,931
  • 1
  • 15
  • 28
  • That was my thought also, buy if my machine has 4 gb ram and i fill that in 4 minutes then the cache will swap to the pagefile when adding more objects to the cache, instead of removing the LRU objects. It will first take action when the sliding expiration is reached. – aweis Mar 11 '12 at 10:01
  • I've updated the answer to include how this could be addressed. Size limit will dump objects one limit is reached. Paging cannot be prevented completely. – saarp Mar 11 '12 at 18:47
  • For some reasone it does not change the property value. When looking into the cache object after instanciating it the limit is still 98! – aweis Mar 11 '12 at 20:01
  • After a bit more debugging i found out that the attribut is called `physicalMemoryLimitPercentage` - this will change the default value. – aweis Mar 11 '12 at 20:03
  • After some tests i still have no change in the memory limits, i've added some code to the question with dummy data filling a cache, and there are still no removal of items. – aweis Mar 11 '12 at 20:33
  • 2
    Hrm. Seems this class is straight-up broken. This thread has some interesting discussion about the particulars - http://stackoverflow.com/questions/6895956/memorycache-strangeness. You may have to roll your own or find another cache implementation. – saarp Mar 13 '12 at 07:52