У меня есть серверное приложение .NET Core 2.0 с пользовательским классом коллекции, похожим на OrderedDictionary, который хранит массив T [] внутри. Многие из них были выделены для каждого запроса к приложению, поэтому они занимали много оперативной памяти и иногда вызывали огромные сборки мусора 2-го поколения, которые замораживали все приложение на 10-30 секунд. Многие из этих массивов были достаточно большими, чтобы попасть в кучу больших объектов.
В попытке исправить это, я попытался использовать ArrayPool для повторного использования массивов. Это значительно уменьшило частоту и продолжительность зависаний ГХ, но заставило приложение использовать гораздо больше ОЗУ. Сумма, сообщаемая GC.GetTotalMemory()
, аналогична при использовании ArrayPool
, но значение Private Bytes (в Windows) или RSS + Swap (в Linux) выше на 50-60%. Используя SOS !dumpheap -stat
, я вижу, что есть много «свободных» фрагментов, поэтому это выглядит как фрагментация кучи .NET.
Если я принудительно сжимаю GC с
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(2, GCCollectionMode.Default, true, true);
PrivateЗначение Bytes / RSS + Swap версии ArrayPool снижается примерно до 120% от версии, отличной от ArrayPool, но со временем, когда приложение обрабатывает больше запросов, оно снова достигает 150%.
Почему этопроисходит и как я могу предотвратить эту потерю оперативной памяти?