Перейти к содержанию

Начало урока

Управление памятью вручную

Берём выделение памяти под контроль: арены, пулы, placement new и выравнивание

Главная идея: Глобальные new/delete и malloc/free - это аллокаторы общего назначения: они обязаны обслуживать любой размер, любое время жизни и любой поток. За эту универсальность приходится платить временем, кэшем и фрагментацией. Пользовательский аллокатор сознательно отказывается от универсальности - вы знаете шаблон выделения памяти в своей программе, поэтому можете возвращать память быстрее и плотнее, чем это когда-либо сделает аллокатор общего назначения. Эта тема о том, как отделить выделение памяти от конструирования и взять под контроль и то, и другое.
К концу вы сможете:
  • объяснить, почему универсальная куча медленна и фрагментирует память,
  • использовать placement new, чтобы сконструировать объект в уже принадлежащей вам памяти, и разрушить его вручную,
  • построить арену (bump-аллокатор) и пул фиксированного размера и понимать, когда уместен каждый,
  • рассуждать о выравнивании с помощью alignof, alignas и std::align,
  • подключить собственный ресурс памяти к STL через C++17 <memory_resource>.

1. Чем плоха универсальная куча

Каждый вызов ::operator new или malloc может делать неожиданно много работы: искать блок в списке свободных участков или в корзинах классов размеров, разбивать или сливать блоки, обновлять служебные заголовки и - при конкуренции - брать блокировку. Возвращаемый блок несёт скрытые накладные расходы на каждое выделение (слово размера/заголовка, округление до класса размера) и оказывается там, где аллокатор нашёл место, а не рядом с соседями. Для продвинутого кода важны три цены:

  • Задержка: одно выделение может стоить сотни наносекунд; в горячем цикле это доминирует.
  • Фрагментация: после множества разноразмерных выделений и освобождений свободная память дробится на слишком мелкие для переиспользования кусочки. Внешняя фрагментация тратит место между блоками; внутренняя - на дополнении внутри округлённого блока.
  • Кэш-локальность: объекты, используемые вместе, но выделенные далеко друг от друга, вызывают промахи кэша. Пользовательский аллокатор может уложить их подряд.

Решение - не "написать более быстрый malloc". Оно в том, чтобы использовать шаблон, о котором аллокатор общего назначения не может знать: "все эти объекты умирают одновременно" (арена) или "каждый объект ровно одного размера" (пул). Когда шаблон выполняется, аллокатор сводится к сдвигу указателя или снятию элемента со списка - несколько инструкций, без поиска, без блокировки.

Продолжите этот урок на YourAIZone

В полном уроке — объяснение целиком, разобранные примеры, частые ошибки, тесты с мгновенной проверкой и ИИ-репетитор. Начните сразу как гость или создайте аккаунт, чтобы сохранять прогресс.

В полном уроке

  • Объяснение целиком
  • Разобранные примеры
  • Частые ошибки и как их избежать
  • Тестовые вопросы: 15
  • ИИ-репетитор, который отвечает на вопросы по теме
  • Отслеживание прогресса для ученика, родителей и учителя