如何获取System.Runtime.Caching.MemoryCache当前缓存大小及使用率
查询System.Runtime.Caching.MemoryCache实时内存使用占比的方法
官方公开API没有直接暴露实时内存使用量的查询接口,但是MemoryCache内部本身维护了内存使用的估算统计值,可以通过反射读取内部字段计算占比,以下是可直接落地的实现方案。
核心实现逻辑
- MemoryCache内部通过私有字段维护当前缓存的近似内存占用值,这个值也是框架本身触发内存淘汰逻辑的判断依据,统计精度足够用于占比计算。
- 缓存实际生效的内存上限是
CacheMemoryLimitMegabytes配置的私有内存阈值、PhysicalMemoryLimitPercentage配置的物理内存占比阈值两者的最小值,任意一个阈值触发都会启动缓存淘汰。 - 通过反射读取内部字段时提前缓存反射元数据,避免每次查询重复执行反射操作产生性能损耗。
可直接复用的代码
using System; using System.Reflection; using System.Runtime.Caching; public static class MemoryCacheExtensions { // 提前缓存反射获取的字段、方法元数据,降低性能开销 private static readonly FieldInfo _sizedRefField = typeof(MemoryCache).GetField("_sizedRef", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly MethodInfo _approximateSizeMethod = _sizedRefField?.FieldType.GetMethod("GetApproximateSize", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo _cacheMemoryLimitField = typeof(MemoryCache).GetField("_cacheMemoryLimit", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo _physicalMemoryLimitField = typeof(MemoryCache).GetField("_physicalMemoryLimit", BindingFlags.Instance | BindingFlags.NonPublic); /// <summary> /// 获取当前缓存估算的已使用内存字节数 /// </summary> public static long GetCurrentMemoryUsageBytes(this MemoryCache cache) { if (cache == null) throw new ArgumentNullException(nameof(cache)); if (_sizedRefField == null || _approximateSizeMethod == null) throw new NotSupportedException("当前运行时版本不支持读取MemoryCache内存统计值"); var sizedRef = _sizedRefField.GetValue(cache); if (sizedRef == null) return 0; long size = (long)_approximateSizeMethod.Invoke(sizedRef, null); return size < 0 ? 0 : size; } /// <summary> /// 获取当前缓存实际生效的内存上限字节数 /// </summary> public static long GetEffectiveMemoryLimitBytes(this MemoryCache cache) { if (cache == null) throw new ArgumentNullException(nameof(cache)); long configMemLimit = _cacheMemoryLimitField?.GetValue(cache) is long limit1 ? limit1 : long.MaxValue; long physicalMemLimit = _physicalMemoryLimitField?.GetValue(cache) is long limit2 ? limit2 : long.MaxValue; return Math.Min(configMemLimit, physicalMemLimit); } /// <summary> /// 获取当前缓存已使用内存占生效上限的比例,返回值范围0~1 /// </summary> public static double GetMemoryUsageRatio(this MemoryCache cache) { long used = cache.GetCurrentMemoryUsageBytes(); long limit = cache.GetEffectiveMemoryLimitBytes(); if (limit <= 0) return 0; double ratio = (double)used / limit; return ratio > 1 ? 1 : ratio; } }
使用方式
直接在MemoryCache实例上调用扩展方法即可,支持默认实例和自定义命名实例:
// 查询默认内存缓存实例的使用占比 double usageRatio = MemoryCache.Default.GetMemoryUsageRatio(); // 换算为百分比:usageRatio * 100 // 单独查询已使用字节数、上限字节数 long usedBytes = MemoryCache.Default.GetCurrentMemoryUsageBytes(); long limitBytes = MemoryCache.Default.GetEffectiveMemoryLimitBytes();
注意事项
- 拿到的内存使用量是近似估算值,不是字节级精确统计。这是MemoryCache的原生设计:写入条目时累加估算的条目大小,移除条目时扣减对应大小,不会实时遍历对象计算精确内存占用,框架自带的内存淘汰逻辑也是基于这个估算值运行,完全可以满足监控内存占用水平的需求。
- 上述代码适配.NET Framework 4.7+、.NET Core 3.1、.NET 5/6/7版本,如果后续大版本更新修改了内部私有字段命名,只需要对应调整反射读取的字段名即可,核心逻辑不变。
- 不要过于频繁调用查询方法,建议监控场景下间隔数秒查询一次即可,虽然反射开销已经被缓存,但过于频繁的查询还是会产生不必要的性能消耗。
内容的提问来源于stack exchange,提问作者Tiger Galo
相关产品推荐
相关产品推荐

