c# .net

c# - ¿Cómo medir el tamaño actual de.NET Memory Cache 4.0?



(4)

Como alternativa, también podría implementar la interfaz IMemoryCacheManager y asignarla a la propiedad global ObjectCache.Host . Eso requiere que tenga permiso para hacerlo, es decir, ningún otro componente de su aplicación ya lo ha hecho (ASP.NET me viene a la mente, pero no estoy seguro). Personalmente, utilizo ese enfoque en una aplicación de Servicio de Consola / Windows sin problemas.

Tenga en cuenta también que solo obtendrá tamaños de caché después de un GC completo o algo así, pero eso no debería ser diferente con el enfoque de Hans .

También tenga en cuenta que el código siguiente funciona para los MemoryCaches nombrados, es decir, no en la instancia en sí.

Toda una vista "pero" s. Pero entonces, no requiere reflexión.

Entonces, aquí está el código.

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

Actualmente estamos utilizando .NET Memory Cache 4.0 para los requisitos de almacenamiento en caché. (no caché de ASP.NET, no cualquier caché externo)

En cuanto a los contadores de rendimiento ''.NET Memory Cache 4.0'', hay datos sobre los aciertos, faltas, entradas, ajustes, etc. de caché, pero nada relacionado con el tamaño.

¿Existe alguna forma de medir / conocer el tamaño actual de la memoria caché utilizada por la aplicación de producción?

Quiero poder capturar estos datos en varios puntos en el tiempo y obtener el tamaño promedio de la memoria caché.


Es un detalle de implementación feo que Microsoft no quiso exponer en absoluto. Medir el tamaño de los objetos en .NET no es posible en general. MemoryCache usa una puerta trasera bastante desagradable para implementar su activador de límite de memoria, usa el componente DACCESS del CLR, en realidad destinado a ayudar a implementar perfiladores de memoria.

Puede verlo con el depurador por lo que no es como si no pudiera acceder a él. Solo tienes que escribir código muy feo para cavar a través de los campos privados:

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

Parecía funcionar bastante bien en .NET 4.6.1, no probado extensivamente. Esto está bien para obtener la disposición del terreno, pero no dependa de él ya que puede fallar con cualquier actualización de .NET.


Si busca Memoria en el rendimiento, hay algunos contadores que necesita usar como:

Bytes en todos los montones

Total de bytes reservados

Puede usar Perfmon para hacer esto, y puede hacer referencia a este enlace:

http://msdn.microsoft.com/en-us/library/x2tyfybc(v=vs.110).aspx

Pero no es fácil de medir de esta manera, pero tendrá una buena visión.

Roberto


Tomé el código original y tuve que hacer un pequeño ajuste, usé "_sizedRefMultiple" en lugar de "_sizedRef" para hacerlo funcionar con .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); } }