Шрифт:
Интервал:
Закладка:
Предположим, что в управляемой куче находится набор объектов с именами А, В, С, D, E, F и G. Во время сборки мусора эти объекты (а также любые внутренние объектные ссылки, которые они могут содержать) будут проверяться. После построения графа недостижимые объекты (пусть ими будут объекты С и F) помечаются как являющиеся мусором. На рис. 9.3 показан возможный граф объектов для только что описанного сценария (линии со стрелками можно читать как "зависит от" или "требует", т.е. Е зависит от G и В, А не зависит ни от чего и т.д.).
После того как объекты помечены для уничтожения (в данном случае С и F, т.к. они не учтены в графе объектов), они удаляются из памяти. Оставшееся пространство в куче сжимается, что в свою очередь вынуждает исполняющую среду изменить лежащие в основе указатели для ссылки на корректные местоположения в памяти (это делается автоматически и прозрачно). И последнее, но не менее важное действие — указатель на следующий объект перенастраивается так, чтобы указывать на следующую доступную область памяти. Конечный результат описанных изменений представлен на рис. 9.4.
На заметку! Строго говоря, сборщик мусора использует две отдельные кучи, одна из которых предназначена специально для хранения крупных объектов. Во время сборки мусора обращение к данной куче производится менее часто из-за возможного снижения производительности, связанного с перемещением больших объектов. В .NET Core куча для хранения крупных объектов может быть уплотнена по запросу или при достижении необязательных жестких границ, устанавливающих абсолютную или процентную степень использования памяти.
Понятие поколений объектов
Когда исполняющая среда пытается найти недостижимые объекты, она не проверяет буквально каждый объект, помещенный в управляемую кучу. Очевидно, это потребовало бы значительного времени, тем более в крупных (т.е. реальных) приложениях.
Для содействия оптимизации процесса каждому объекту в куче назначается специфичное "поколение". Лежащая в основе поколений идея проста: чем дольше объект существует в куче, тем выше вероятность того, что он там будет оставаться. Например, класс, который определяет главное окно настольного приложения, будет находиться в памяти вплоть до завершения приложения. С другой стороны объекты, которые были помещены в кучу только недавно (такие как объект, размещенный внутри области действия метода), по всей видимости, довольно быстро станут недостижимыми. Исходя из таких предположений, каждый объект в куче принадлежит совокупности одного из перечисленных ниже поколений.
• Поколение 0. Идентифицирует новый размещенный в памяти объект, который еще никогда не помечался как подлежащий сборке мусора (за исключением крупных объектов, изначально помещаемых в совокупность поколения 2). Большинство объектов утилизируются сборщиком мусора в поколении 0 и не доживают до поколения 1.
• Поколение 1. Идентифицирует объект, который уже пережил одну сборку мусора. Это поколение также служит буфером между кратко и длительно существующими объектами.
• Поколение 2. Идентифицирует объект, которому удалось пережить более одной очистки сборщиком мусора, или весьма крупный объект, появившийся в совокупности поколения 2.
На заметку! Поколения 0 и 1 называются эфемерными (недолговечными). В следующем разделе будет показано, что процесс сборки мусора трактует эфемерные поколения по-разному.
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})Сначала сборщик мусора исследует все объекты, относящиеся к поколению 0. Если пометка и удаление (или освобождение) таких объектов в результате обеспечивают требуемый объем свободной памяти, то любые уцелевшие объекты повышаются до поколения 1. Чтобы увидеть, каким образом поколение объекта влияет на процесс сборки мусора, взгляните на рис. 9.5, где схематически показано, как набор уцелевших объектов поколения 0 (А, В и E) повышается до следующего поколения после восстановления требуемого объема памяти.
Если все объекты поколения 0 проверены, но по-прежнему требуется дополнительная память, тогда начинают исследоваться на предмет достижимости и подвергаться сборке мусора объекты поколения 1. Уцелевшие объекты поколения 1 повышаются до поколения 2. Если же сборщику мусора все еще требуется дополнительная память, то начинают проверяться объекты поколения 2. На этом этапе объекты поколения 2, которым удается пережить сборку мусора, остаются объектами того же поколения 2, учитывая заранее определенный верхний предел поколений объектов.
В заключение следует отметить, что за счет назначения объектам в куче определенного поколения более новые объекты (такие как локальные переменные) будут удаляться быстрее, в то время как более старые (наподобие главного окна приложения) будут существовать дольше.
Сборка мусора инициируется, когда в системе оказывается мало физической памяти, когда объем памяти, выделенной в физической куче, превышает приемлемый порог или когда в коде приложения вызывается метод GC.Collect().
Если все описанное выглядит слегка удивительным и более совершенным, чем необходимость в самостоятельном управлении памятью, тогда имейте в виду, что процесс сборки мусора не обходится без определенных затрат. Время сборки мусора и то, что будет подвергаться сборке, обычно не находится под контролем разработчиков, хотя сборка мусора безусловно может расцениваться положительно или отрицательно. Кроме того, выполнение сборки мусора приводит к расходу циклов центрального процессора (ЦП) и может повлиять на производительность приложения. В последующих разделах исследуются различные типы сборки мусора.
Эфемерные поколения и сегменты
Как упоминалось ранее, поколения 0 и 1 существуют недолго и называются эфемерными поколениями. Эти поколения размещаются в памяти, которая известна как эфемерный сегмент. Когда происходит сборка мусора, запрошенные сборщиком мусора новые сегменты становятся новыми эфемерными сегментами, а сегменты, содержащие объекты, которые уцелели в прошедшем поколении 1, образуют новый сегмент поколения 2. Размер эфемерного сегмента зависит от ряда факторов, таких как тип сборки мусора (рассматривается следующим) и разрядность системы. Размеры эфемерных сегментов описаны в табл. 9.1.
Типы сборки мусора
Исполняющая среда поддерживает два описанных ниже типа сборки мусора.
• Сборка мусора на рабочей станции. Тип сборки мусора, который спроектирован для клиентских приложений и является стандартным для автономных приложений. Сборка мусора на рабочей станции может быть фоновой (обсуждается ниже) или выполняться в непараллельном режиме.
• Сборка мусора на сервере. Тип сборки мусора, спроектированный для серверных приложений, которым требуется высокая производительность и масштабируемость. Подобно сборке мусора на рабочей станции сборка мусора на сервере может быть фоновой или выполняться в непараллельном режиме.
На заметку! Названия служат признаком стандартных настроек для приложений рабочей станции и сервера, но метод сборки мусора можно настраивать через файл runtimeconfig.json или переменные среды системы. При наличии на компьютере только одного ЦП будет всегда использоваться сборка мусора на рабочей станции.
- Введение в Perl - Маслов Владимир Викторович - Базы данных