Как измерить текущий размер of.NET кэш памяти 4.0?
В настоящее время мы используем .NET Memory Cache 4.0 для требований к кэшированию. (нет ASP.NET кэш, а не какой-либо внешний кэш)
Глядя на счетчики производительности ".NET Memory Cache 4.0", есть данные о попаданиях в кэш, промахах, записях, обрезках и т. д. но ничего, связанного с размером.
Существует ли способ измерения/знания текущего размера кэша, используемого производственным приложением?
Я хочу иметь возможность захватить эти данные в различные моменты времени и получите средний размер кэша.
4 ответов:
Это уродливая деталь реализации, которую Microsoft вообще не хотела раскрывать. Измерение размеров объектов в .NET вообще невозможно. MemoryCache использует довольно неприятный бэкдор для реализации своего триггера ограничения памяти, он использует компонент DACCESS среды CLR, фактически предназначенный для реализации профилировщиков памяти.
Вы можете увидеть его с помощью отладчика, так что это не похоже на то, что вы не можете добраться до него. Вам просто нужно написать очень уродливый код, чтобы копаться в частном поля:
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.
Я взял исходный код и должен был сделать небольшую корректировку, я использовал "_sizedRefMultiple" вместо "_sizedRef", чтобы заставить его работать с .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); } }
В качестве альтернативы можно также реализовать интерфейс
IMemoryCacheManagerи присвоить его глобальному свойствуObjectCache.Host. Это требует, чтобы вам было разрешено сделать это, то есть ни один другой компонент в вашем приложении еще не сделал этого (ASP.NET приходит на ум, но я не уверен). Лично я использую этот подход в приложении-службе консоли/Windows без проблем.Также обратите внимание, что вы получите размеры кэша только после полного GC или около того, но это не должно отличаться от Hans' подход .
Также обратите внимание, что приведенный ниже код работает для именованных кэшей памяти, т. е. не на самом экземпляре.
Вполне себе точка зрения " но " с. Но тогда она не требует рефлексии.
Итак, вот код.
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; } }
Если вы ищете память в производительности, есть некоторые счетчики, которые вам нужно использовать, как:
Байты во всех кучах
Всего зарезервированных байт
Вы можете использовать Perfmon для этого, и вы можете ссылаться на эту ссылку:
Http://msdn.microsoft.com/en-us/library/x2tyfybc (v=против 110).aspx
Но это не просто сделать таким образом, но у вас будет хорошее понимание.
Роберто
Comments