Как измерить текущий размер of.NET кэш памяти 4.0?



В настоящее время мы используем .NET Memory Cache 4.0 для требований к кэшированию. (нет ASP.NET кэш, а не какой-либо внешний кэш)



Глядя на счетчики производительности ".NET Memory Cache 4.0", есть данные о попаданиях в кэш, промахах, записях, обрезках и т. д. но ничего, связанного с размером.



Существует ли способ измерения/знания текущего размера кэша, используемого производственным приложением?



Я хочу иметь возможность захватить эти данные в различные моменты времени и получите средний размер кэша.

418   4  

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

    Ничего не найдено.