目前我们正在使用.NET Memory Cache 4.0来满足缓存要求。 (不是ASP.NET缓存,不是任何外部缓存)
查看“.NET内存缓存4.0”性能计数器,有关于缓存命中,未命中,条目,修剪等的数据,但与尺寸无关。
是否有一种方法可以衡量/了解生产应用程序使用的缓存的当前大小?
我希望能够在不同的时间点捕获这些数据,并获得缓存的平均大小。
答案 0 :(得分:22)
这是一个丑陋的实现细节,微软根本不想公开。在.NET中测量对象大小通常是不可能的。 MemoryCache使用一个非常讨厌的后门来实现其内存限制触发器,它使用CLR的DACCESS组件,实际上旨在帮助实现内存分析器。
你可以通过调试器看到它,所以它不能达到你的目的。你只需编写非常难看的代码来挖掘私有领域:
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);
}
}
似乎在.NET 4.6.1上工作得很好,没有经过广泛测试。这是可以获得的土地,只是不依赖它,因为它可能会破坏任何.NET更新。
答案 1 :(得分:7)
我使用原始代码并进行了微调,我使用了" _sizedRefMultiple" 而不是" _sizedRef" 使其适用于 .NET 4.6 。
Public Sub test3()
Dim i As Integer
With Sheet10
.Cells(3, 13).Formula = "=0"
For i = 1 To 100
If .Cells(1, 22 * i - 8) <> 0 Then
.Cells(3, 13).Formula = .Cells(3, 13).Formula & "+" & .Cells(3, 22 * i + 2).Address
End If
If .Cells(1, 22 * i + 3) <> 0 Then
.Cells(3, 13).Formula = .Cells(3, 13).Formula & "+" & .Cells(3, 22 * i + 13).Address
End If
Next i
End With
End Sub
答案 2 :(得分:2)
作为替代方案,您还可以实现IMemoryCacheManager
接口并将其分配给全局ObjectCache.Host
属性。
这要求您可以这样做,即您的应用程序中没有其他组件已经这样做了(ASP.NET会想到,但我不确定)。就个人而言,我在控制台/ Windows服务应用程序中使用该方法没有问题。
另请注意,您只能在完整GC之后获得缓存大小,但与Hans' approach不应该有任何不同。
另请注意,以下代码适用于命名的MemoryCaches,即不在实例本身上。
相当一种观点“但是”。但是,它不需要反思。
所以,这是代码。
public static class MemoryCacheHelper
{
private static readonly MemoryCacheServiceProvider s_serviceProvider = new MemoryCacheServiceProvider();
static MemoryCacheHelper()
{
try
{
ObjectCache.Host = s_serviceProvider;
}
catch (InvalidOperationException ex)
{
// ObjectCache.Host can only be set once.
}
}
public static MemoryCache Create(string name, NameValueCollection config)
{
return new MemoryCache(name, config);
}
// Return approximate cache size and when that value was last determined.
public static Tuple<long, DateTime> GetApproximateSize(string name)
{
return s_serviceProvider.GetApproximateSize(cache.Name);
}
private class MemoryCacheServiceProvider : IMemoryCacheManager, IServiceProvider
{
private readonly object m_lock = new object();
private readonly IDictionary<string, Tuple<long, DateTime>> m_sizes = new Dictionary<string, Tuple<long, DateTime>>();
public Tuple<long, DateTime> GetApproximateSize(string name)
{
lock (m_lock)
{
Tuple<long, DateTime> info;
if (m_sizes.TryGetValue(name, out info))
return info;
return null;
}
}
void IMemoryCacheManager.UpdateCacheSize(long size, MemoryCache cache)
{
lock (m_lock)
{
// The UpdateCacheSize() method will be called based on the configured "pollingInterval"
// for the respective cache. That value defaults to 2 minutes. So this statement doesn't
// fire to often and as a positive side effect we get some sort of "size-heartbeat" which
// might help when troubleshooting.
m_sizes[cache.Name] = Tuple.Create(size, DateTime.UtcNow);
}
}
void IMemoryCacheManager.ReleaseCache(MemoryCache cache)
{
lock (m_lock)
{
m_sizes.Remove(cache.Name);
}
}
object IServiceProvider.GetService(Type serviceType)
{
if (serviceType == typeof(IMemoryCacheManager))
{
return this;
}
return null;
}
}
答案 3 :(得分:0)
我写了一个扩展方法,该方法迭代对象的公共属性并总结大小值。如果它是IEnumerable或Array,它将遍历项目并计算其大小。这不是最好的方法,但是对我来说足够好,因为对我的缓存没有比这更复杂的要求了。
注意:您应该将其添加到计算中的每个缓存项的开销似乎约为600字节。
public static class ObjectMemorySizeCalculator
{
static int OBJECT_SIZE = IntPtr.Size == 8 ? 24 : 12;
static int POINTER_SIZE = IntPtr.Size;
public static long GetMemorySize(this object obj)
{
long memorySize = 0;
Type objType = obj.GetType();
if (objType.IsValueType)
{
memorySize = Marshal.SizeOf(obj);
}
else if (objType.Equals(typeof(string)))
{
var str = (string)obj;
memorySize = str.Length * 2 + 6 + OBJECT_SIZE;
}
else if (objType.IsArray)
{
var arr = (Array)obj;
var elementType = objType.GetElementType();
if (elementType.IsValueType)
{
long elementSize = Marshal.SizeOf(elementType);
long elementCount = arr.LongLength;
memorySize += elementSize * elementCount;
}
else
{
foreach (var element in arr)
{
memorySize += element != null ? element.GetMemorySize() + POINTER_SIZE : POINTER_SIZE;
}
}
memorySize += OBJECT_SIZE + 40;
}
else if (obj is IEnumerable)
{
var enumerable = (IEnumerable)obj;
foreach(var item in enumerable)
{
var itemType = item.GetType();
memorySize += item != null ? item.GetMemorySize() : 0;
if (itemType.IsClass)
memorySize += POINTER_SIZE;
}
memorySize += OBJECT_SIZE;
}
else if (objType.IsClass)
{
var properties = objType.GetProperties();
foreach (var property in properties)
{
var valueObject = property.GetValue(obj);
memorySize += valueObject != null ? valueObject.GetMemorySize() : 0;
if (property.GetType().IsClass)
memorySize += POINTER_SIZE;
}
memorySize += OBJECT_SIZE;
}
return memorySize;
}