How to measure current size of .NET Memory Cache 4.0?
It is an ugly implementation detail that Microsoft did not want to expose at all. Measuring object sizes in .NET is not in general possible. MemoryCache uses a fairly nasty backdoor to implement its memory limit trigger, it uses the DACCESS component of the CLR, actually intended to help implement memory profilers.
You can see it with the debugger so it isn't like you can't get to it. You just have to write very ugly code to dig through the private fields:
using System;
using System.Reflection;
using System.Runtime.Caching;
public static class MemoryCacheHackExtensions {
public static long GetApproximateSize(this MemoryCache cache) {
var statsField = typeof(MemoryCache).GetField("_stats", BindingFlags.NonPublic | BindingFlags.Instance);
var statsValue = statsField.GetValue(cache);
var monitorField = statsValue.GetType().GetField("_cacheMemoryMonitor", BindingFlags.NonPublic | BindingFlags.Instance);
var monitorValue = monitorField.GetValue(statsValue);
var sizeField = monitorValue.GetType().GetField("_sizedRef", BindingFlags.NonPublic | BindingFlags.Instance);
var sizeValue = sizeField.GetValue(monitorValue);
var approxProp = sizeValue.GetType().GetProperty("ApproximateSize", BindingFlags.NonPublic | BindingFlags.Instance);
return (long)approxProp.GetValue(sizeValue, null);
}
}
Seemed to work pretty well on .NET 4.6.1, not extensively tested. This is okay to get the lay of the land, just don't depend on it since it may break with any .NET update.
I took the original code and had to make a minor adjustment, I used "_sizedRefMultiple" instead of "_sizedRef" to make it work with .NET 4.6.
public static class MemoryCacheHackExtensions
{
public static long GetApproximateSize(this MemoryCache cache)
{
var statsField = typeof(MemoryCache).GetField("_stats", BindingFlags.NonPublic | BindingFlags.Instance);
var statsValue = statsField.GetValue(cache);
var monitorField = statsValue.GetType().GetField("_cacheMemoryMonitor", BindingFlags.NonPublic | BindingFlags.Instance);
var monitorValue = monitorField.GetValue(statsValue);
var sizeField = monitorValue.GetType().GetField("_sizedRefMultiple", BindingFlags.NonPublic | BindingFlags.Instance);
var sizeValue = sizeField.GetValue(monitorValue);
var approxProp = sizeValue.GetType().GetProperty("ApproximateSize", BindingFlags.NonPublic | BindingFlags.Instance);
return (long)approxProp.GetValue(sizeValue, null);
}
}