Default memory cache with LRU policy

aweis picture aweis · Mar 11, 2012 · Viewed 7.8k times · Source

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());
    }
}

Answer

saarp picture saarp · Mar 11, 2012

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#.