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

Я читал о кучах Generations и Large object. Но я все еще не понимаю, в чем смысл (или польза) наличия большой кучи объектов?

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

Ответ 1

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

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

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

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

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

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


UPDATE,.NET 4.5.1 теперь поддерживает уплотнение свойства LOH, GCSettings.LargeObjectHeapCompactionMode. Остерегайтесь последствий.

Ответ 2

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

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

Ответ 3

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

  • Размещение объектов (небольшие объекты не смешиваются с большими объектами)
  • Сбор мусора (LOH собран только на полном GC)
  • Дефрагментация памяти (LOH - никогда редко сжимается)

Ответ 4

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

Ответ 5

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