Виды аллокаторов из стандартной библиотеки
Один мастер выдаёт отдельные коробки и принимает каждую назад. Другой отдаёт целую тележку для одного заказа и забирает её в конце. Третий предлагает ровно один ящик фиксированного размера. Все выдают место, но стоимость и правила уборки различаются.
Выбор аллокатора похож на выбор этой организации работы. Сегодня попробуем фиксированный буфер и арену, не меняя интерфейс функции, которая просит память.
Одна мысль урока
Один интерфейс аллокатора позволяет менять стратегию памяти под срок жизни задачи.
Опора из прошлого урока: Аллокаторы: явное управление памятью.
От картинки к записи
std.heap.FixedBufferAllocator работает поверх переданного массива. Новые блоки занимают место внутри него. Если запрос не помещается, возвращается OutOfMemory. Такой источник удобен, когда верхняя граница памяти известна и нужна предсказуемость. Он не обещает найти дополнительную системную память после заполнения.
Арена
std.heap.ArenaAllocator получает дочерний аллокатор, запрашивает у него большие участки и выдаёт
из них небольшие блоки. В конце arena.deinit() возвращает участки. Это удобно для группы объектов
с общим сроком жизни: разобрал документ, обработал, уничтожил всё сразу.
Арена не делает старые указатели безопасными после deinit. Она лишь упрощает совместную уборку. Если объект нужно сохранить дольше остальных, его нельзя оставлять в уже уничтоженной арене. Можно заранее выбрать отдельный источник или сделать независимую копию, но это отдельное действие.
std.heap.DebugAllocator(.{}) помогает искать ошибки: он хранит служебные сведения о выделениях и сообщает об утечках. У его экземпляра есть метод allocator(), который даёт общий интерфейс. Метод deinit() возвращает состояние, которое можно проверить или явно отбросить. В тестах удобнее готовый std.testing.allocator, поэтому не создаём дополнительный отладочный экземпляр без причины.
std.heap.page_allocator работает с памятью через страницы операционной системы. Страница это сравнительно крупная единица выдачи памяти. Такой источник прост как основа для других стратегий, но не обязательно выгоден для множества крошечных выделений. Нельзя объявить его «лучшим» только потому, что пример короткий.
Важно различать free отдельного блока и освобождение всего источника. У разных стратегий возврат блока может не немедленно уменьшить фактическую память процесса. У арены отдельные освобождения не дают обещания вернуть все занятые страницы. Смысл общего deinit остаётся основным договором.
В Zig 0.16 нет необходимости переписывать функцию duplicate ради смены стратегии: она принимает std.mem.Allocator. Вызов передаёт fixed.allocator() либо arena.allocator(). Это пример полезной абстракции: она скрывает способ выдачи места, но не скрывает факт выделения и ответственность за срок жизни.
Не начинай оптимизацию с выбора экзотического источника. Сначала посчитай, какие данные создаются вместе, кто их читает и когда всё можно убрать. Если срок жизни каждой записи отдельный, одна гигантская арена может удерживать лишнюю память. Если весь разбор живёт один запрос, она может существенно упростить очистку.
премиум-урок
Продолжение в премиуме: 1 200 ₽ в месяц или 10 000 ₽ в год. Вместе с ним серверные проверки задач и школьные токены AI-коуча.
Оплата пока не подключена: премиум и курсы открываются промокодом.