В этой записи нашего блога мы хотели бы обсудить некоторые значительные изменения, которые были сделаны в сборщике мусора (GC от англ. garbage collector) .NET 4.6.х. Мы рекомендуем Вам использовать самую последнюю версию, 4.6.2.

Наш главный разработчик, Маони Стивенс описала улучшения, которые были реализованы в 4.6.2. фреймворке. Эти изменения были сделаны для того, чтобы улучшить производительность фреймворка и позволить сборщику мусора работать более эффективно.

Как сделать закрепление объектов более эффективным

В предыдущих версиях .NET Framework, когда объект определялся как прошедший проверку закреплённый объект, мы не могли перемещать этот объект и смежные ему существующие объекты. Поскольку мы оставляем закрепленные объекты в поколениях 0 и 1 (таким образом вы сможете вскоре использовать свободное пространство между ними), это значит, что нам необходимо просматривать их во время выполнения сборки мусора. Если бы многие другие объекты вокруг закрепленных объектов пережили бы процесс сборки мусора, тогда время сбора мусора в 0 и 1 поколениях могло бы быть больше желаемого. Начиная с версии 4.6.2, это ограничение было отменено, так что мы можем собирать смежные существующие объекты вокруг закрепленных объектов. Во время тестирования мы заметили впечатляющие улучшения времени сбора в сценариях, где сборщик мусора искусственным образом закреплял многие объекты.

На рисунке Before два не закрепленных объекта собираются, но не закрепленный объект рядом с закрепленным остается в gen0.

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

Если вы закрепляете много объектов, например, при длительных операциях ввода-ввывода, при этом наблюдаете длинные паузы сборщика мусора, вы, скорее всего, извлечете выгоду из этого изменения.

Более эффективное использование свободного пространства во втором поколении

В предшествующих версиях фреймворка при переносе объектов из поколения 1 в поколение 2 использовалась техника first fit, а это означало, что GC не использовал память, которая не подходила для хранения объекта. Эта техника приводила к потере памяти.

На рисунке Before, мы видим, что F0 область освобождена, потому что была слишком мала для того , чтобы сохранить выживший объект S. Мы смогли поместить S в F1 при этом часть F1 остается (для дальнейшего уплотнения памяти при переносе из gen1).

Чтобы улучшить этот подход и более эффективно использовать память, мы ввели список, в котором мы размещаем объекты слева направо в свободном пространстве в сегменте, который подходит по размеру. Мы должны очень аккуратно относиться к использованию маленьких сегментов, так как их может быть очень много. Также, мы работали над политиками определения сегмента, который следует попробовать использовать для хранения объекта, так как мі не хотим продлевать сборку мусора в первом поколении поиском свободного подходящего места в памяти. Мы хотим продолжать делать сборку мусора в фоновом режиме где это возможно, чтобы более эффективно использовать свободное пространство.

На картинке After F0 остается в свободном списке и может быть использован для уплотнения памяти объектами из gen1. И мы сразу пробуем поместить S в F1. Если вы наблюдаете увеличение памяти gen2, то одной из причин может быть то, что у вас закончились элементы в свободно списке. Следовательно, продвижение оставшихся объектов из gen1 потребует увеличение размера gen2. Для сценариев, которым помогает это изменение, вы увидите, что размер gen2 увеличивается намного медленнее, и мы сможем разместить больше коллекций gen1 для каждой коллекции gen2. Это также означает, что вы будете видеть блокировку gen2 реже, потому что, когда куча становится слишком большой, это приводит к чрезмерной загрузке памяти, мы будем блокировать gen2 и выполнять уплотнение. В наших тестах, мы наблюдали сценарии, в которых соотношение количества сборов в поколениях gen1 и gen2 возросло в 20 к 1 -  более чем 200 сборам для поколения gen1 для каждого сбора в поколении gen2.

Резюме

Если вы столкнулись с ситуациями, описанными выше, вам обязательно нужно попробовать .NET 4.6.2, чтобы получить преимущества улучшений в сборщике мусора.

Источник