Объекты в.NET MemoryCache неожиданно выселяются

У меня странное поведение с использованием.NET MemoryCache в приложении ASP.NET. Проблема в том, что объекты будут выселены через несколько минут, и, похоже, для этого нет причин. Ограничения памяти устанавливаются в файле web.config:

  <system.runtime.caching>
    <memoryCache>
      <namedCaches>
        <add name="Default"
             cacheMemoryLimitMegabytes="1500"
             physicalMemoryLimitPercentage="18"
             pollingInterval="00:02:00" />
      </namedCaches>
    </memoryCache>
  </system.runtime.caching>

Моя машина разработки имеет 8 ГБ оперативной памяти, а процесс w3wp.exe использует около 0,5 ГБ. 2 ГБ по-прежнему доступны на компьютере во время работы приложения (кроме Visual Studio, веб-браузеров и т. Д.)

Метод RemovedCallBack был добавлен к каждой записи для создания записей журнала для каждого удаления и особенно для выселений:

   private static void CachedItemRemovedCallback(CacheEntryRemovedArguments arguments)
    {
        LogCurrentCacheDelta(arguments.CacheItem, true);

        if (arguments.RemovedReason == CacheEntryRemovedReason.Evicted)
        {
            Sitecore.Diagnostics.Log.Warn(
                string.Format(
                    "Cache Item Evicted (cacheMemoryLimitMegabytes: {0}) - Key: {1}, Value: {2}",
                    FlightServiceCache.CacheMemoryLimit,
                    arguments.CacheItem.Key,
                    arguments.CacheItem.Value),
                FlightServiceCache);
        }
    }

Счетчик для расчета размера в настоящее время также был реализован. Я использую двоичную сериализацию, чтобы оценить размер объектов в памяти. На данный момент произошло первое выселение, в кеше было около 120 объектов, а использованная память была около 6 мегабайт. Насколько я понимаю, это никоим образом не является причиной для удаления записей из кэша. Но это происходит снова и снова, и после нескольких дней расследования я все еще не уверен, почему это происходит.

Я также взглянул на внутреннюю реализацию функции trim() в исходном коде платформы.NET, используемой при выселении объектов. Таким образом, вычисление не легко понять, может быть, кто-то знает, как оно работает, и может указать мне на это.

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

Заранее большое спасибо и извините за действительно длинный пост;)

(кстати, это мой первый пост, поэтому любые предложения о том, как улучшить мои вопросы, будут высоко оценены)

1 ответ

Была точно такая же проблема. Даже если я установлю для CacheMemoryLimit достаточно большое значение (скажем, 1 ГБ), а для PhysicalMemoryLimit — 10% (что в моем случае с 32 ГБ физической установленной памяти составляет 3,2 ГБ), все равно многие из моих записей в кеше будут удалены, чтобы освободить кеш. Память. Обратите внимание, что я кэшировал 1 МБ элементов и 10 из них, так что всего 10 МБ, тогда как я предполагал, что у меня будет хотя бы минимальное из двух ограничений, упомянутых выше, а именно 1 ГБ.

Да, @VMAtm был прав в своем комментарии выше, что следует использовать больший%, и я тестировал с 10%, что он вытеснил, с 50% - нет, и с помощью метода "разделяй и властвуй" я доказал, что с моей настройкой тогда около 45% он уже не выселяет. Но обратите внимание, что в зависимости от общего объема установленной памяти поведение может отличаться для значений %, которые я использовал для тестирования.

Поэтому для меня смысл был не в том, чтобы доверять PhysicalMemoryLimit % и не устанавливать его, а в том, чтобы использовать только свойство конфигурации CacheMemoryLimit. И если вам все же нужно доказать вариант с PhysicalMemoryLimit%, то вместо использования настроек конфигурации system.runtime.caching скорее введите свои собственные настройки, прочитайте их, получите фактический размер физической установленной памяти, используйте свою настройку в процентах, а затем рассчитайте минимум из двух: предел физической памяти (теперь в байтах) и предел кэш-памяти (в байтах из ваших собственных настроек). Имея это, вы можете затем создать MemoryCache и передать только конфигурацию cacheMemoryLimitMegabytes через NameValueCollection в его конструктор, а значение для свойства конфигурации cacheMemoryLimitMegabytes будет минимум из двух, рассчитанных выше.

Кстати, чтобы получить общий объем установленной физической памяти, можно использовать:

      [DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetPhysicallyInstalledSystemMemory(out long totalMemoryInKilobytes);
Другие вопросы по тегам