Почему большой кусок объекта и почему мы заботимся?
Я читал о кучах Generations и Large. Но я до сих пор не понимаю, в чем заключается значимость (или польза) наличия большой кучи объектов?
Что могло бы пойти не так (с точки зрения производительности или памяти), если бы CLR просто полагалась на поколение 2 (учитывая, что порог для Gen0 и Gen1 мал для обработки больших объектов) для хранения больших объектов?
- Явная терминология кучи: молодые, старые и постоянные поколения?
- Распределение памяти / освобождение Узкое место?
- Верхний предел GC превышен
- Каковы корни?
- Java 7 (JDK 7) garbage collection и документация по G1
Сбор мусора не только избавляет от неописанных объектов, но и уплотняет кучу. Это очень важная оптимизация. Это не только повышает эффективность использования памяти (без неиспользуемых отверстий), но и делает кеш процессора более эффективным. Кэш очень интересен для современных процессоров, они на порядок быстрее, чем шина памяти.
Уплотнение выполняется просто путем копирования байтов. Это требует времени. Чем больше объект, тем больше вероятность того, что стоимость его копирования перевешивает возможные улучшения использования кэша ЦП.
Поэтому они проверили множество тестов, чтобы определить точку безубыточности. И достиг 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 . Остерегайтесь последствий, пожалуйста.
Если размер объекта больше некоторого закрепленного значения (85000 байт в .NET 1), CLR помещает его в кучу больших объектов. Это оптимизирует:
- Размещение объектов (небольшие объекты не смешиваются с большими объектами)
- Сбор мусора (LOH собирался только на полную ГК)
- Деfragmentация памяти (LOH
никогда небывает компактным)
Существенное отличие кучи объектов (SOH) и больших объектов (LOH) заключается в том, что память в SOH уплотняется при сборке, а LOH – нет, как иллюстрирует эта статья . Стоит многокомпактных больших объектов. Как и в примерах в статье, скажем, для перемещения байта в памяти требуется 2 цикла, тогда для уплотнения объекта размером 8 МБ на компьютере с частотой 2 ГГц требуется 8 мс, что является большой стоимостью. Учитывая, что большие объекты (массивы в большинстве случаев) довольно распространены на практике, я полагаю, именно поэтому Microsoft связывает большие объекты в памяти и предлагает LOH.
Кстати, согласно этому сообщению , LOH обычно не создает проблем с fragmentами памяти.
Принципиальным является то, что это маловероятно (и, вполне возможно, плохой дизайн), что процесс создаст много короткоживущих больших объектов, поэтому среда CLR выделяет большие объекты в отдельную кучу, на которой она запускает GC по другому расписанию в обычную кучу. http://msdn.microsoft.com/en-us/magazine/cc534993.aspx
Я не эксперт по CLR, но я бы предположил, что наличие выделенной кучи для больших объектов может предотвратить ненужные просчеты GC существующих поколений. Выделение большого объекта требует значительного количества непрерывной свободной памяти. Чтобы обеспечить, что из разбросанных «дыр» в кучах поколений вам понадобятся частые сбои (которые выполняются только с циклами GC).