Loment
RU

Система

LomentGC

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

Читать статью

Лестница

От сильнейшего к слабейшему: компилятор берёт самую сильную ступень, какую может доказать. Докажешь больше — платишь меньше. Цена каждой ступени написана рядом с ней, а не оставлена в сноске.

L0 — статическая

Размер выделения — литерал, и указатель всегда передаётся целиком как первый аргумент загрузки, записи или атомарной операции. Он вообще не доходит до кучи, так что возвращать нечего. Цена: более крупный кадр стека.

L1 — последнее использование

Та же форма, объявленная на верхнем уровне тела функции, поэтому последнее использование исчислимо. Компилятор вставляет free перед самым ранним возвратом после него. Цена: на пути с ранним возвратом точка вставки может быть пропущена, и этот путь течёт один раз. Утечка, а не ошибка.

L2 — эпохи блоков

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

L3 — этап выполнения

Всё, что компилятор не смог доказать. Полная цена сборщика.

Три уровня

Программа выбирает, как далеко вниз по лестнице она готова пойти, одной строкой в корневом модуле.

choose gc_manual

Значение по умолчанию. Ничего не возвращается автоматически; программа управляет своей памятью сама. Если не написать ни строки, получите именно это — значение по умолчанию есть поведение, которое было до появления измерения, и это правило соблюдает каждое измерение.

choose gc_auto

Сборщик этапа выполнения возвращает память: маркировка и очистка с точными корнями, запускаемая по числу выделенных байт.

choose gc_auto_alpha

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

Предел, который назван, а не обнаружен. При gc_auto триггер — фиксированные 32 KiB, тогда как арена — 64 KiB, поэтому программа, чей живой набор превышает примерно 32 KB, всё равно исчерпает память. Закрыть этот разрыв — то, для чего нужны адаптивный сборщик и поколенческий под gc_auto_alpha.

Внутри L3

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

Маркировка и очистка

Корни точные — таблица корней хранит адреса слотов, — тогда как внутренность блока трактуется консервативно, потому что alloc нетипизирован и ничто не записывает, что хранит блок.

Адаптивный запуск

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

Поколенческий

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

Почему это попадает в артефакт

Каждый модуль экспортирует форму-объект, несущий gc_ladder: сколько участков alloc взяла каждая ступень, плюс всего. Четыре счётчика должны в сумме давать итог, и два независимых валидатора проверяют эту арифметику, не читая ни строки исходника. Так что «как управляется память этой программы?» — это число в выводе сборки, а не утверждение в документе.

Что утверждается, а что нет. Лестница реализована дважды — эталонным компилятором и самоподдерживающимся (self-hosted) — и эти двое должны совпадать побайтово. Измерения за ступенями идут из собственных критериев проекта, а это небольшие программы; сборщик не запускался на реальной нагрузке, и это стоит знать, прежде чем читать всё это как утверждение о производительности.