Что такое сборщик мусора c
Перейти к содержимому

Что такое сборщик мусора c

  • автор:

Сборка мусора

Сборщик мусора .NET управляет выделением и освобождением памяти для приложения. При каждом создании объекта среда CLR выделяет память для объекта из управляемой кучи. Пока в управляемой куче есть доступное адресное пространство, среда выполнения продолжает выделять пространство для новых объектов. Тем не менее ресурсы памяти не безграничны. В конечном счете сборщику мусора необходимо выполнить сбор, чтобы освободить память. Механизм оптимизации сборщика мусора определяет наилучшее время для выполнения сбора, основываясь на выполненных операциях выделения памяти. Когда сборщик мусора выполняет сборку, он проверяет наличие объектов в управляемой куче, которые больше не используются приложением, а затем выполняет необходимые операции, чтобы освободить память.

Заголовок Описание
Основы сборки мусора Описание работы сборки мусора, выделения объектов в управляемой куче и других базовых понятий.
Сборка мусора рабочей станции и сборка мусора сервера Описывает различия между сборкой мусора рабочей станции для клиентских приложений и сборкой мусора сервера для серверных приложений.
Фоновая сборка мусора Описывает фоновую сборку мусора, которая представляет собой сборку объектов поколения 0 и 1 во время сборки объектов поколения 2.
Куча больших объектов Описывает кучу больших объектов (LOH) и то, как для них выполняется сборка мусора.
Сборка мусора и производительность Проверки производительности, которые можно использовать для диагностики проблем со сборкой мусора и производительностью.
Индуцированные коллекции Описание выполнения сборки мусора.
Режимы задержки Описание режимов, которые определяют степень вмешательства сборщика мусора.
Оптимизация совместного размещения веб-сайтов Способы оптимизации сборки мусора на серверах, совместно используемыми небольшими веб-узлами.
Уведомления о сборке мусора Определение необходимости полной сборки мусора и времени завершения этой операции.
Отслеживание ресурсов домена приложения Способы наблюдения за использованием ЦП и памяти доменом приложения.
Слабые ссылки Описание функциональных возможностей, которые позволяют сборщику мусора обрабатывать объект, разрешая при этом приложению получать доступ к этому объекту.

Справочник

  • System.GC
  • System.GCCollectionMode
  • System.GCNotificationStatus
  • System.Runtime.GCLatencyMode
  • System.Runtime.GCSettings
  • GCSettings.LargeObjectHeapCompactionMode
  • Object.Finalize
  • System.IDisposable

См. также

Дополнительные ресурсы

Значок отказа согласно Закону Калифорнии о защите конфиденциальности потребителей (CCPA)

  • Светлая
  • Темная
  • Высокая контрастность
  • Предыдущие версии
  • Блог
  • Участие в доработке
  • Конфиденциальность
  • Условия использования
  • Товарные знаки
  • © Microsoft 2023

Дополнительные ресурсы

Значок отказа согласно Закону Калифорнии о защите конфиденциальности потребителей (CCPA)

  • Светлая
  • Темная
  • Высокая контрастность
  • Предыдущие версии
  • Блог
  • Участие в доработке
  • Конфиденциальность
  • Условия использования
  • Товарные знаки
  • © Microsoft 2023

Сборка мусора, управление памятью и указатели

Ранее в теме Типы значений и ссылочные типы мы рассматривали отдельные типы данных и как они располагаются в памяти. Так, при использовании переменных типов значений в методе, все значения этих переменных попадают в стек. После завершения работы метода стек очищается.

При использовании же ссылочных типов, например, объектов классов, для них также будет отводиться место в стеке, только там будет храниться не значение, а адрес на участок памяти в хипе или куче, в котором и будут находиться сами значения данного объекта. И если объект класса перестает использоваться, то при очистке стека ссылка на участок памяти также очищается, однако это не приводит к немедленной очистке самого участка памяти в куче. Впоследствии сборщик мусора (garbage collector) увидит, что на данный участок памяти больше нет ссылок, и очистит его.

Test(); void Test() < Person tom = new Person("Tom"); Console.WriteLine(tom.Name); >record class Person(string Name);

В методе Test создается объект Person. С помощью оператора new в куче для хранения объекта CLR выделяет участок памяти. А в стек добавляет адрес на этот участок памяти. В неявно определенном методе Main мы вызываем метод Test. И после того, как Test отработает, место в стеке очищается, а сборщик мусора очищает ранее выделенный под хранение объекта Person участок памяти.

Сборщик мусора не запускается сразу после удаления из стека ссылки на объект, размещенный в куче. Он запускается в то время, когда среда CLR обнаружит в этом потребность, например, когда программе требуется дополнительная память.

Как правило, объекты в куче располагаются неупорядочено, между ними могут иметься пустоты. Куча довольно сильно фрагментирована. Поэтому после очистки памяти в результате очередной сборки мусора оставшиеся объекты перемещаются в один непрерывный блок памяти. Вместе с этим происходит обновление ссылок, чтобы они правильно указывали на новые адреса объектов.

Так же надо отметить, что для крупных объектов существует своя куча — Large Object Heap . В эту кучу помещаются объекты, размер которых больше 85 000 байт. Особенность этой кучи состоит в том, что при сборке мусора сжатие памяти не проводится по причине больших издержек, связанных с размером объектов.

Несмотря на то что, на сжатие занятого пространства требуется время, да и приложение не сможет продолжать работу, пока не отработает сборщик мусора, однако благодаря подобному подходу также происходит оптимизация приложения. Теперь чтобы найти свободное место в куче среде CLR не надо искать островки пустого пространства среди занятых блоков. Ей достаточно обратиться к указателю кучи, который указывает на свободный участок памяти, что уменьшает количество обращений к памяти.

Кроме того, чтобы снизить издержки от работы сборщика мусора, все объекты в куче разделяются по поколениям. Всего существует три поколения объектов: 0, 1 и 2-е.

К поколению 0 относятся новые объекты, которые еще ни разу не подвергались сборке мусора. К поколению 1 относятся объекты, которые пережили одну сборку, а к поколению 2 — объекты, прошедшие более одной сборки мусора.

Когда сборщик мусора приступает к работе, он сначала анализирует объекты из поколения 0. Те объекты, которые остаются актуальными после очистки, повышаются до поколения 1.

Если после обработки объектов поколения 0 все еще необходима дополнительная память, то сборщик мусора приступает к объектам из поколения 1. Те объекты, на которые уже нет ссылок, уничтожаются, а те, которые по-прежнему актуальны, повышаются до поколения 2.

Поскольку объекты из поколения 0 являются более молодыми и нередко находятся в адресном пространстве памяти рядом друг с другом, то их удаление проходит с наименьшими издержками.

Класс System.GC

Функционал сборщика мусора в библиотеке классов .NET представляет класс System.GC . Через статические методы данный класс позволяет обращаться к сборщику мусора. Как правило, надобность в применении этого класса отсутствует. Наиболее распространенным случаем его использования является сборка мусора при работе с неуправляемыми ресурсами, при интенсивном выделении больших объемов памяти, при которых необходимо такое же быстрое их освобождение.

Рассмотрим некоторые методы и свойства класса System.GC:

  • Метод AddMemoryPressure информирует среду CLR о выделении большого объема неуправляемой памяти, которую надо учесть при планировании сборки мусора. В связке с этим методом используется метод RemoveMemoryPressure , который указывает CLR, что ранее выделенная память освобождена, и ее не надо учитывать при сборке мусора.
  • Метод Collect приводит в действие механизм сборки мусора. Перегруженные версии метода позволяют указать поколение объектов, вплоть до которого надо произвести сборку мусора
  • Метод GetGeneration(Object) позволяет определить номер поколения, к которому относится переданый в качестве параметра объект
  • Метод GetTotalMemory возвращает объем памяти в байтах, которое занято в управляемой куче
  • Метод WaitForPendingFinalizers приостанавливает работу текущего потока до освобождения всех объектов, для которых производится сборка мусора

Работать с методами System.GC несложно:

// . long totalMemory = GC.GetTotalMemory(false); GC.Collect(); GC.WaitForPendingFinalizers(); //.

С помощью перегруженных версий метода GC.Collect можно выполнить более точную настройку сборки мусора. Так, его перегруженная версия принимает в качестве параметра число — номер поколения, вплоть до которого надо выполнить очистку. Например, GC.Collect(0) — удаляются только объекты поколения 0.

Еще одна перегруженная версия принимает еще и второй параметр — перечисление GCCollectionMode . Это перечисление может принимать три значения:

  • Default : значение по умолчанию для данного перечисления (Forced)
  • Forced : вызывает немедленное выполнение сборки мусора
  • Optimized : позволяет сборщику мусора определить, является ли текущий момент оптимальным для сборки мусора

Например, немедленная сборка мусора вплоть до первого поколения объектов: GC.Collect(1, GCCollectionMode.Forced);

Что такое сборщик мусора c

В программировании сборка мусора (устоявшийся термин, с точки зрения русского языка правильнее «сбор мусора», англ. garbage collection, GC) — одна из форм автоматического управления памятью. Специальный код, называемый сборщиком мусора (garbage collector), периодически освобождает память, удаляя объекты, которые уже не будут востребованы приложением — то есть производит сборку мусора.

Цитата из Википедии

Посмотрите на следующий код:

Много раз мы анализировали работу подобного кода по шагам:

  1. Создаётся переменная с именем x (ячейка фиксированной длины в памяти)
  2. Где-то в памяти создаётся объект
  3. Ссылка на созданный объект помещается в переменную x

Все ясно, правда?

А теперь, следом за первой строкой кода запишем вторую:

Здесь тоже все понятно: число 1 записывается в переменную x (в ту ячейку памяти фиксированной длины, которая отведена под x ).

А что случилось со ссылкой на объект? Понятно, что. Она пропала, затерлась единицей. А что случилось с самим объектом?

Предположим, что объект остался в памяти. Но этот объект — явный мусор: нет переменных, которые на него ссылаются, значит, к нему никак нельзя обратиться из программы.

Хорошо бы такие объекты удалять из памяти, освобождая пространство для новых нужд. Иначе во время работы программы может наступить момент, когда мусором будет забита вся память.

Конечно, мусор надо убирать. За чистотой в доме следит сборщик мусора — составная часть интерпретатора JavaScript.

Область памяти Ссылки

У сборщика мусора есть «журнал» учета памяти.

Область памяти Ссылки
x

Вид журнала (условно) после выполнения первой строки:

Область памяти Ссылки
нет ссылок

Вид журнала (условно) после выполнения второй строки:

Сборщик мусора, просматривая журнал, обнаружит, что на область памяти, отведенную под объект , нет ни одной ссылки. Объект будет уничтожен, свободная память пополнится. Сборщик мусора отложит метлу и выпьет стакан чаю.

А теперь посмотрите на следующий код:

; var y = x; x = 1; 

Есть работа для сборщика мусора? Нет, он отдыхает. В самом деле, посмотрим, как заполняется журнал учета памяти.

После первой команды:

Область памяти Ссылки
x

Сборщик мусора

В этой и последующих статьях мы займемся исследованием , одного из основных механизмов, оказывающих существенное влияние на производительность приложений для .NET. Освобождая разработчиков от хлопот, связанных с освобождением памяти, сборщик мусора вводит другие проблемы, которые необходимо учитывать при разработке программ, производительность которых имеет большое значение.

Назначение сборщика мусора

Сборка мусора — это высокоуровневая абстракция, избавляющая разработчиков от необходимости заботиться об освобождении управляемой памяти. В окружениях, снабженных механизмом сборки мусора, выделение памяти производится в момент создания объектов, а освобождение происходит, когда в программе исчезает последняя ссылка на объект. Кроме того, сборщик мусора предоставляет интерфейс финализации для неуправляемых ресурсов, находящихся за пределами управляемой динамической памяти, благодаря чему имеется возможность обеспечить выполнение кода, когда эти ресурсы окажутся не нужны.

При создании сборщика мусора в .NET преследовались две основные цели:

  • избавиться от ошибок и ловушек, связанных с управлением памятью вручную;
  • обеспечить производительность операций управления памятью, равную или превышающую производительность ручных механизмов.

В существующих языках программирования и фреймворках используются различные стратегии управления памятью. Мы коротко исследуем две из них: управление на основе списка свободных блоков (реализацию которой можно найти в коллекции стандартных инструментов управления памятью языка C) и сборка мусора на основе подсчета ссылок.

Управление свободным списком

Управление на основе списка свободных блоков — это механизм управления распределением памяти в стандартной библиотеке языка C, который также по умолчанию используется функциями управления памятью в C++, такими как new и delete. Это детерминированный диспетчер памяти, при использовании которого вся ответственность за выделение и освобождение памяти ложится на плечи разработчика. Свободные блоки памяти хранятся в виде связанного списка, откуда изымаются блоки памяти при выделении, и куда они возвращаются, при освобождении.

Диспетчер памяти хранит список свободных блоков

Он изымает блоки из списка при выделении памяти и возвращает их обратно при освобождении. Приложение обычно получает блоки памяти, хранящие их размеры в служебной области.

Механизм управления памятью на основе списка не свободен от тактических и стратегических решений, влияющих на производительность приложения. Ниже перечислены некоторые из них:

  1. Приложение, использующее механизм управления памятью на основе списка свободных блоков, изначально получает небольшой пул свободных блоков, организованных в виде списка. Список может быть отсортирован по размеру, по времени использования и так далее.
  2. Когда диспетчер получает от приложения запрос на выделение памяти, он выполняет поиск соответствующего блока памяти. Соответствие может определяться по принципу «первый подходящий», «лучше подходящий» или с применением более сложных критериев.
  3. После исчерпания списка диспетчер запрашивает у операционной системы дополнительные свободные блоки и добавляет их в список. Когда приложение возвращает память диспетчеру, он добавляет освободившийся блок в список. На этом этапе может выполняться слияние смежных свободных блоков, дефрагментация и сокращение списка, и так далее.

Ниже перечислены основные проблемы, связанные с управлением памятью на основе списка свободных блоков:

  • Высокая стоимость операции выделения: поиск блока, соответствующего параметрам запроса, требует времени, даже при использовании критерия «первый подходящий». Кроме того, блоки часто разбиваются на несколько частей. В многопроцессорных системах неизбежно возникает конкуренция за список и необходимость синхронизации операций, если только не используется несколько списков. С другой стороны, наличие нескольких списков ухудшает их фрагментацию.
  • Высокая стоимость освобождения: возврат блока в список требует времени, и здесь снова возникает проблема синхронизации конкурирующих операций освобождения памяти.
  • Высокая стоимость управления: чтобы избежать ситуации отсутствия блоков памяти подходящего размера при наличии большого количества маленьких блоков, необходимо выполнять дефрагментацию списка. Но эта работа должна производиться в отдельном потоке выполнения, что опять же требует применения блокировок для доступа к списку и снижает скорость операций выделения и освобождения памяти. Фрагментацию можно уменьшить, выделяя блоки фиксированного размера и поддерживая несколько списков, но при этом увеличивается количество операций по поддержанию динамической памяти в целостном состоянии и добавляет накладные расходы к каждой операции выделения и освобождения памяти.

Сборка мусора на основе подсчета ссылок

Сборщик мусора, опирающийся на подсчет ссылок, связывает каждый объект с целочисленной переменной — счетчиком ссылок. В момент создания объекта счетчик ссылок инициализируется значением 1. Когда приложение создает новую ссылку на объект, его счетчик ссылок увеличивается на 1. Когда приложение удаляет ссылку на существующий объект, его счетчик ссылок уменьшается на 1. Когда счетчик ссылок достигает значения 0, объект можно уничтожить и освободить занимаемую им память.

Каждый объект содержит счетчик ссылок

Одним из примеров управления памятью на основе подсчета ссылок в экосистеме Windows является объектная модель программных компонентов (Component Object Model, COM). Объекты COM снабжаются счетчиками ссылок, определяющими продолжительность их существования. Когда значение счетчика ссылок достигает 0, объект может освободить занимаемую им память. Основное бремя подсчета ссылок ложится на плечи разработчика, в виде явного вызова методов AddRef() и Release(), хотя в большинстве языков имеются обертки, автоматизирующие вызовы этих методов при создании и удалении ссылок.

Ниже перечислены основные проблемы, связанные с управлением памятью на основе подсчета ссылок:

  • Высокая стоимость управления: всякий раз, когда создается или уничтожается ссылка на объект, необходимо обновлять счетчик ссылок. Это означает, что к стоимости обновления ссылки прибавляются накладные расходы на выполнение таких тривиальных операций, как присваивание ссылки (a = b) или передача ссылки в функцию по значению. В многопроцессорных системах выполнение таких операций требует применения механизмов синхронизации и вызывает «пробуксовку» кеша процессора, при попытке нескольких потоков выполнения одновременно изменить счетчик ссылок.
  • Использование памяти: счетчик ссылок на объект должен храниться в памяти объекта. Это на несколько байтов увеличивает объем памяти, занимаемой объектом, что делает подсчет ссылок нецелесообразным для легковесных объектов. (Впрочем, это не такая большая проблема для CLR, где к каждому объекту «в нагрузку» добавляется от 8 до 16 байт.)
  • Правильность: при управлении памятью на основе подсчета ссылок возникает проблема утилизации объектов с циклическими ссылками. Если приложение больше не ссылается на некоторую пару объектов, но каждый из них продолжает хранить ссылку на другой объект (как показано на рисунке ниже), возникает утечка памяти. Эта проблема описывается в документации COM, где явно оговаривается, что такие циклические ссылки должны уничтожаться вручную. Другие платформы, такие как язык программирования Python, поддерживают дополнительные механизмы определения циклических ссылок и их устранения, применение которых влечет за собой увеличение стоимости сборки мусора.

Уничтожение циклических ссылок

В следующей статье мы рассмотрим механизм сборки мусора на основе трассировки, который используется в .NET.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *