Подтвердить что ты не робот

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

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

Глядя на счетчики производительности .NET Memory Cache 4.0, есть данные вокруг Cache Hits, Misses, Entries, Trims и т.д., но ничего не связано с Size.

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

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

4b9b3361

Ответ 1

Это уродливая деталь реализации, которую 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.

Ответ 2

Я взял исходный код и должен был выполнить небольшую корректировку, я использовал "_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);
    }
}

Ответ 3

В качестве альтернативы вы также можете реализовать интерфейс IMemoryCacheManager и назначить его глобальному свойству ObjectCache.Host. Для этого вам необходимо сделать это, т.е. Ни один другой компонент в вашем приложении уже не сделал этого (ASP.NET приходит на ум, но я не уверен). Лично я использую этот подход в приложении Console/Windows Service без проблем.

Заметьте также, что вы получите только размеры кеша после полного GC или около того, но это не должно отличаться от подхода Hans..

Также обратите внимание, что приведенный ниже код работает для именованных 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;
        }
    }

Ответ 4

Если вы ищете память в производительности, есть несколько счетчиков, которые вам нужно использовать, например:

Байт во всех кучах

Всего зарезервированных байтов

Вы можете использовать Perfmon для этого, и вы можете ссылаться на эту ссылку:

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

Но это не так просто для того, чтобы смириться таким образом, но у вас будет хорошее понимание.

Роберто