Ух ты, поэтому я потратил слишком много времени на копание в CLR с отражателем, но я думаю, что я наконец-то хорошо разбираюсь в том, что здесь происходит.
Настройки читаются правильно,но, похоже, в самой CLR есть глубокая проблема, которая выглядит так, как будто это сделает настройку предела памяти практически бесполезной.
Следующий код отражен из DLL-файла System.Runtime.Caching дляКласс CacheMemoryMonitor (есть аналогичный класс, который контролирует физическую память и работает с другими настройками, но это более важный параметр):
protected override int GetCurrentPressure()
{
int num = GC.CollectionCount(2);
SRef ref2 = this._sizedRef;
if ((num != this._gen2Count) && (ref2 != null))
{
this._gen2Count = num;
this._idx ^= 1;
this._cacheSizeSampleTimes[this._idx] = DateTime.UtcNow;
this._cacheSizeSamples[this._idx] = ref2.ApproximateSize;
IMemoryCacheManager manager = s_memoryCacheManager;
if (manager != null)
{
manager.UpdateCacheSize(this._cacheSizeSamples[this._idx], this._memoryCache);
}
}
if (this._memoryLimit <= 0L)
{
return 0;
}
long num2 = this._cacheSizeSamples[this._idx];
if (num2 > this._memoryLimit)
{
num2 = this._memoryLimit;
}
return (int) ((num2 * 100L) / this._memoryLimit);
}
Первое, что вы можете заметить, это то, что он даже непопробуйте взглянуть на размер кэша до окончания сборки мусора Gen2, вместо этого просто прибегая к существующему значению сохраненного размера в cacheSizeSamples.Таким образом, вы никогда не сможете поразить цель с самого начала, но если все остальное сработает, мы, по крайней мере, получим измерение размера, прежде чем у нас возникнут реальные проблемы.
Итак, предположив, что произошел GC Gen2, мы столкнулись с проблемой 2, которая заключается в том, что ref2.ApproximateSize выполняет ужасную работу, фактически приближая размер кеша.Пробираясь через мусор CLR, я обнаружил, что это System.SizedReference, и это то, что он делает для получения значения (IntPtr - дескриптор самого объекта MemoryCache):
[SecurityCritical]
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern long GetApproximateSizeOfSizedRef(IntPtr h);
Я предполагаю, чтоОбъявление extern означает, что в этот момент он погружается в неуправляемые окна, и я понятия не имею, как начать выяснять, что он там делает.Из того, что я наблюдал, хотя он делает ужасную работу, пытаясь приблизиться к размеру всего.
Третья заметная вещь - это вызов manager.UpdateCacheSize, который, похоже, должен что-то делать.К сожалению, в любом обычном примере того, как это должно работать, s_memoryCacheManager всегда будет нулевым.Поле устанавливается из открытого статического члена ObjectCache.Host.Если пользователь выберет этот вариант, с ним можно будет поиграться, и я действительно смог заставить эту вещь работать так, как и предполагалось, собрав воедино мою собственную реализацию IMemoryCacheManager, установив ее в ObjectCache.Host, а затем выполнив пример,Однако в этот момент кажется, что вы могли бы просто сделать свою собственную реализацию кеша и даже не беспокоиться обо всем этом, тем более, что я понятия не имею, устанавливаю ли ваш собственный класс в ObjectCache.Host (статический, так что это влияет на каждый из них).из них, которые могут быть там в процессе), чтобы измерить кеш, могут испортиться другие вещи.
Я должен верить, что, по крайней мере, часть этого (если не пара частей) является просто ошибкой.Было бы приятно услышать от кого-то из MS, что за сделка была с этой штукой.
TLDR-версия этого гигантского ответа: предположим, что CacheMemoryLimitMegabytes в этот момент полностью отключен.Вы можете установить его на 10 МБ, а затем приступить к заполнению кеша до ~ 2 ГБ и исключить исключение нехватки памяти без отключения удаления элемента.