Файл: Clr компиляция исходного кода в управляемые модули.docx
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 08.11.2023
Просмотров: 40
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
не которые они ссылаются, как достижимые. После завершения этого процесса все непомеченные объекты
считаются недостижимыми и подлежат сборке мусора.
Недостижимые объекты без финализатора уничтожаются немедленно, а объекты с финализатором помещаются в очередь финализации,
для выполнения их финализатров после завершения сборки мусора параллельно с выполнением программы. После завершения потока
финализации эти объекты считаются доступными для сборки мусора при следующем запуске сборщика.
Оставшиеся активные объекты затем сдвигаются в начало хипа (сжатие), освобождая пространство под новые объекты.
Сжатие устраняет фрагментацию памяти и упрощает процесс выделения памяти под новые объекты.
Сборщик мусора поддерживает различные технологии оптимизации, для сокращения времени сборки мусора.
Поколения при сборке мусора
Идея поколений основывается на том, что некоторые объекты имеют краткий жизненный цикл, другие же существуют длительное время и
отслеживать их при каждой сборке мусора необязательно. Сборщик мусора делит хип на три поколения. Недавно созданные объекты относятся
к поколению Gen0, объекты выдержавшие один цикл сборки мусора — к поколению Gen1, а все остальные объекты принадлежат к поколению Gen2.
Для поколения Gen0 среда CLR выделяет относительно небольшой участок памяти (16Мб). Когда эта память заканчивается,
инициируется сборка мусора для поколения Gen0, что происходит довольно часто. Для поколения Gen1 используется похожий лимит
памяти, поэтому сборка мусора для Gen1 тоже происходит довольно часто. Полная же сборка мусора, включающая поколение Gen2,
является довольно медленной и происходит редко.
LOH
Для объектов, размер которых превышает определенный порог (85 000 байт), сборщик мусора использует отдельную область,
называемую хипом для больших объектов — Large Object Heap, LOH. Большие объекты способны быстро заполнить поколение Gen0,
и при отсутствии LOH сборка мусора для этого поколения запускалась бы чаще, снижая производительность.
Область LOH не подвержена сжатию, т.к. перемещение больших объектов является ресурсоемким.
По этой причине выделение памяти под новые объекты является более медленным, т.к. сборщик мусора должен просматривать свободные участки памяти.
Кроме того LOH подвержена фрагментации.
LOH не поддерживает концепцию поколений: все объекты рассматриваются как относящиеся к поколению Gen2.
Параллельная и фоновая сборка мусора
Сборщик мусора блокирует потоки выполнения на период сборки мусора для поколений Gen0 и Gen1.
Однако сборка мусора для поколения Gen2 протекает параллельно или в фоновом режиме. Эта оптимизация актуальна только для рабочих
станций, для серверов сборка поколения Gen2 также блокирует выполнение потоков.
уплотнение отвалов
Как мы знаем из первого поста , память в управляемых кучах может фрагментироваться, оставляя дыры неиспользуемой памяти
между живыми объектами. Вот почему процесс сборки мусора для SOH включает в себя уплотнение , которое представляет собой процесс
удаления дыр памяти в куче . Его часто называют процессом Copy Collection , потому что он основан на поиске мертвых объектов или дыр в памяти (на самом деле это одно и то же — никем не используемых дыр в памяти, присутствующих между живыми объектами) и перезаписывании этих дыр с помощью живые объекты, выделенные позже (следующими) в куче - путем копирования фрагментов живых объектов «вниз в куче».
Результатом процесса уплотнения является непрерывный SOH, в котором объекты размещаются друг над другом без или с минимальным количеством дыр в памяти .
Наконец, адреса к объектам, хранящимся в ссылочных переменных, обновляются, чтобы указывать на правильные, новые места в памяти, занимаемые конкретным объектом.
Лучший выигрыш, который мы получаем от сжатия, заключается в том, что SOH не фрагментируется, поэтому память может использоваться эффективно. С другой стороны, это требует некоторого процессорного времени, что может оказать некоторое влияние на производительность нашего приложения.
По умолчанию LOH не сжимается из-за размера хранящихся в нем объектов (>85 килобайт). Копирование фрагментов таких объектов заняло бы слишком много времени. Вместо этого отслеживаются используемое пространство и дыры в памяти в LOH, и алгоритм выделения корректируется, чтобы попытаться зарезервировать память для новых объектов оптимальным образом (путем поиска наилучших возможных свободных слотов в фрагментированной куче).
Стоит отметить, что, начиная с .NET Framework 4.5.1, сжатие LOH можно включить по запросу с помощью свойства GCSettings.LargeObjectHeapCompactionMode .
Когда работает ГК?
Можно сказать, что GC работает недетерминированным образом. Он запускается в отдельном потоке, когда выполняется одно из следующих условий:
ОС отправляет уведомление о нехватке памяти,
объем памяти, используемый объектами в управляемой куче, превышает определенный порог (который динамически регулируется во время выполнения на основе текущих распределений),
В коде вызывается метод GC.Collect() .
Если вы видите, что GC.Collect() используется непосредственно в коде, почти в каждом случае это означает, что что-то не так с реализацией приложения. Несколько замечательных инженеров Microsoft и разработчиков открытого исходного кода работали над автоматической сборкой мусора, чтобы не запускать ее вручную.????
Исключения из этого правила я бы принял, может быть, некоторые игровые реализации, в которых вы работаете с огромными объектами, и иногда вы хотели бы повлиять на способ очистки памяти этих объектов или их использование при отладке — большинство профилировщиков памяти (например , dotMemory или ANTS Memory Profiler ) форсирует сбор до того, как пользователь сделает снимок памяти. Это разумно, потому что при отладке утечек памяти или проблем вы хотите увидеть, какие объекты остаются выделенными после того, как сборщик мусора выполнит свою очистку.
Также стоит упомянуть, что при запуске сборки мусора все потоки по умолчанию останавливаются, кроме потока, в котором была запущена сборка. Однако это можно настроить, изменив настройки GC для работы в режиме рабочей станции или сервера, а также запустив его одновременно или в фоновом режиме. Существуют разные варианты использования каждой комбинации настроек — подробнее об этом можно узнать здесь и здесь .
JIT компилирует код во время выполенения программы (переводит в машинный код) затем кэширует
машинный код и при повторном использовании использует этот кэш.
Mono представляет собой реализацию Microsoft .NET Framework с открытым исходным кодом,
для кросс-платформенной разработки
При разных версиях транслятора, генерируется разный IL код.
Может случиться так, что память, полученная из операционной системы для LOH, уже используется (подробнее о памяти читайте здесь).
Затем сборщик мусора запрашивает у операционной системы дополнительные сегменты памяти, которые необходимо получить для LOH.
Если это не удается, запускается сбор данных поколения 2 в надежде, что некоторые блоки памяти будут освобождены, и тогда выделение станет возможным.
GC введена оптимизация, которая делает смежные фрагменты свободной памяти “объединенными” вместе
Как GC решает эту проблему? Ну, на самом деле, после множества оптимизаций, внесенных в LOH management в .NET 4.5, GC
выполняет следующие действия, чтобы создать новое выделение большого объекта:
во-первых, GC пытается выделить новые объекты в одну из “дыр” свободного пространства в LOH (довольно просто вычислить,
достаточно ли велик какой-либо фрагмент для хранения объекта, зная диапазоны блоков свободного пространства из таблицы свободного пространства).
если вышеприведенный сбой – сборщик мусора предпочитает размещать новые большие объекты в конце кучи. Несмотря на то, что
для этого может потребоваться запросить у ОС больше сегментов памяти, было обнаружено, что это более простая и менее трудоемкая
операция, чем выполнение полного GC в надежде сначала освободить некоторые фрагменты памяти на LOH.
только если вышеприведенный сбой (LOH не может быть “расширен”) – GC запускает сбор gen 2 в надежде освободить дополнительное
пространство, которое можно было бы использовать для нового выделения.
Ручное уплотнение LOH
Как вы уже должны знать из предыдущей статьи, LOH можно уплотнить программно, установив параметры GCSettings .Свойство
largeobjecttheapcompactionmode. Самый простой способ принудительного уплотнения LOH, как показано
в следующем фрагменте:
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
он изучает, как объекты размещены в памяти, определяя те из них, до которых может добраться
Сами по себе корни не являются объектами, они представляют собой ссылки на объекты. Любой объект, на который ссылается корень
, автоматически переживет следующую сборку мусора. В .NET существует четыре основных вида корней:
Фрагментация кучи
Менее известное ограничение — это куча больших объектов (Large Object Heap, LOH), в
которой размещаются объекты размером от 85000 байт. Куча больших объектов никогда не
уплотняется, соответственно, объекты, которые в ней размещены, никогда не перемещаются, что может привести
к преждевременному исчерпанию памяти в программе. Когда одни объекты живут дольше других, в куче образуются
так называемые дыры — это называется фрагментацией. Проблема возникает, когда программа запрашивает большой блок памяти,
но куча стала настолько фрагментированной, что в ней нет ни одной непрерывной области, достаточно большой, чтобы вместить
его. Исключение OutOfMemoryException, вызванное фрагментацией, обычно происходит, когда программа имеет много свободной памяти,э
но из-за фрагментации не может разместить в ней новый объект.
Производительность сборщика мусора
С точки зрения производительности, наиболее важной особенностью систем с автоматической сборкой мусора является то, что сборщик может начать выполнение в любое время. Это делает такие системы непригодными для использования в случаях, когда время выполнения критически важно, поскольку любая операция может быть приостановлена работой сборщика мусора.
Режимы работы сборщика мусора
Сборщик мусора в .NET имеет два основных режима работы: режим рабочей станции и режим сервера, а также два подрежима: параллельный и непараллельный. Параллельный режим рабочей станции используется в настольных приложениях, а режим сервера — в серверных, например, в ASP.NET.
В параллельном режиме рабочей станции, .NET пытается избежать долгой приостановки приложения за счет того, что параллельно с работой потоков программы, в фоновом режиме работает и поток сборщика мусора, который находит объекты для уничтожения. Это означает, что общий объем работы, которую сборщик может выполнить за определенный промежуток времени, будет меньше, но и приложение не будет на долго останавливаться. Такой подход хорош для интерактивных приложений, где важно создать у пользователя впечатление, что программа реагирует немедленно.
В непараллельном серверном режиме, .NET приостанавливает работу приложения на время работы сборщика мусора. В целом это более эффективно, чем параллельный режим — сборка мусора занимает столько же времени, но при этом ей не приходится бороться с продолжающей работать программой. Однако, при выполнении полной сборки могут возникать заметные паузы.
Режим сборки мусора можно задать в конфигурационном файле приложения, если значение по умолчанию не подходит. Выбор непараллельного режима сборки может быть полезен, когда важнее, чтобы приложение имело высокую пропускную способность, а не выглядело отзывчивым.
Заблуждения про .Net
1. Поколения в GC 3 для CLR/Core CLR, но для Mono всего 2 поколения nursery и old generation (в новеньком и модном сборщике мусора — SGen).
Всё зависит от рантайма.
2. 2 стадии сборки мусора. На самом деле 5 фаз: маркировки, планирования, обновления ссылок, компактинга (удаление с перемещением) и
удаление без перемещений(перевести это дело сложно). Но формально это можно разделить на 3 этапа — маркировка, планирование, чистка.
На этапе маркировки выясняется, какие объекты не стоит собирать сборщику.
На этапе планирования производятся расчет различных показателей текущего состояния памяти и сбор данных,
необходимых на этапе чистки. Благодаря информации, полученной на этом этапе, выносится решение о необходимости
компактинга (дефрагментации), там же просчитывается, на сколько необходимо двигать объекты и т.д.