Документация Engee

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

Введение

В Julia реализован неперемещающий сборщик мусора, работающий по алгоритму маркировки и очистки и учитывающий поколения объектов. К собственным объектам применяется точное сканирование, а внешние помечаются консервативно.

Расположение объектов в памяти и биты сборки мусора

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

Объекты выравниваются по адресам, кратным 4 байтам, чтобы такие теги указателей были допустимы.

Выделение в пуле

Достаточно небольшие объекты (до 2032 байтов) размещаются в пуле.

Для хранения метаданных о диапазонах адресов, охватывающих по крайней мере одну страницу (например, была ли страница выделена, содержит ли она помеченные объекты, сколько всего свободных объектов и т. д.), применяется трехуровневое дерево (аналогичное трехуровневой таблице страниц). Очистка размещенного в пуле объекта заключается в помещении его обратно в список свободной памяти соответствующего пула.

Размещенные в памяти массивы и большие объекты

Для отслеживания остальных размещенных в памяти объектов используются два списка: один для достаточно больших размещенных в памяти массивов (mallocarray_t), а другой для достаточно больших объектов (bigval_t).

Очистка таких объектов заключается в их исключении из соответствующего списка и вызове free для соответствующего адреса.

Наборы с учетом поколения и запоминаемые наборы

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

Явного флага, определяющего то, будет ли при маркировочном проходе сканироваться вся куча или только новые объекты и запоминаемый набор, нет. Для определения того, производится ли полная маркировка, применяются сами биты пометки объектов. Алгоритм маркировки и очистки выполняется в следующем порядке.

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

  • Корни (например, локальные объекты потока) помещаются в очередь.

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

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

  • Задаются биты пометки объектов в запоминаемом наборе, чтобы барьер записи не активировался для них снова.

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

Эвристика

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