Почему куча больших объектов и почему мы заботимся?

Я читал о Поколениях и Большой куче объектов. Но я до сих пор не понимаю, каково значение (или выгода) наличия кучи больших объектов?

Что могло пойти не так (с точки зрения производительности или памяти), если бы CLR просто полагался на поколение 2 (учитывая, что порог для Gen0 и Gen1 мал для обработки больших объектов) для хранения больших объектов?

5 ответов

Решение

Сборка мусора не только избавляется от объектов, на которые нет ссылок, но и сокращает кучу. Это очень важная оптимизация. Это не только делает использование памяти более эффективным (без неиспользуемых отверстий), но и делает кэш-память процессора более эффективной. Кэш-память - это действительно большая проблема для современных процессоров, они на порядок проще, чем шина памяти.

Сжатие выполняется просто путем копирования байтов. Это, однако, требует времени. Чем больше объект, тем больше вероятность того, что стоимость его копирования превысит возможные улучшения использования кэша ЦП.

Таким образом, они провели ряд тестов, чтобы определить точку безубыточности. И достиг 85 000 байтов в качестве точки отсечения, где копирование больше не улучшает производительность. За исключением двух массивов, они считаются "большими", когда массив содержит более 1000 элементов. Это еще одна оптимизация для 32-битного кода, распределитель кучи больших объектов обладает специальным свойством, которое распределяет память по адресам, которые выровнены по 8, в отличие от обычного распределителя поколений, который распределяет только по 4. Чтение или запись неверно выровненного двойника очень дорого. Как ни странно, в редкой информации Microsoft никогда не упоминаются массивы длинных, не знаю, что с этим случилось.

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

Проблема, которая в противном случае полностью исчезает, просто запустив код в 64-битной операционной системе. 64-разрядный процесс имеет 8 терабайт доступного адресного пространства виртуальной памяти, что на 3 порядка больше, чем 32-разрядный процесс. Вы просто не можете выбежать из дыр.

Короче говоря, LOH делает код более эффективным. За счет использования доступной виртуальной памяти адресное пространство менее эффективно.


ОБНОВЛЕНИЕ, .NET 4.5.1 теперь поддерживает сжатие свойства LOH, GCSettings.LargeObjectHeapCompactionMode. Остерегайтесь последствий, пожалуйста.

Существенная разница между кучей малых объектов (SOH) и кучей больших объектов (LOH) заключается в том, что память в SOH сжимается при сборе, а LOH нет, как показано в этой статье. Уплотнение крупных объектов стоит дорого. Как и в примерах, приведенных в статье, скажем, что для перемещения байта в памяти требуется 2 цикла, а затем для сжатия 8-мегабайтного объекта в 2-ГГц компьютере требуется 8 мс, что является большой ценой. Учитывая, что крупные объекты (в большинстве случаев массивы) довольно распространены на практике, я полагаю, что именно поэтому Microsoft закрепляет большие объекты в памяти и предлагает LOH.

Кстати, согласно этому посту, LOH обычно не генерирует проблемы с фрагментами памяти.

Если размер объекта больше некоторого закрепленного значения (85000 байт в.NET 1), то CLR помещает его в кучу больших объектов. Это оптимизирует:

  1. Распределение объектов (маленькие объекты не смешиваются с большими объектами)
  2. Сборка мусора (LOH собирается только на полной GC)
  3. Дефрагментация памяти (LOH никогда редко уплотняется)

Принцип состоит в том, что маловероятно (и вполне возможно, плохой дизайн), что процесс будет создавать множество короткоживущих крупных объектов, поэтому CLR выделяет большие объекты в отдельную кучу, в которой он запускает GC по другому расписанию, чем обычная куча. http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

Я не эксперт по CLR, но я бы предположил, что выделенная куча для больших объектов может предотвратить ненужные развертки GC существующих куч кучи. Выделение большого объекта требует значительного количества непрерывной свободной памяти. Чтобы обеспечить это из рассеянных "дыр" в кучах поколений, вам понадобятся частые уплотнения (которые делаются только с циклами GC).

Другие вопросы по тегам