Что такое фрагментация кучи (heap fragmentation) в C++?

«Что такое фрагментация кучи (heap fragmentation) в C++?» — вопрос из категории Управление памятью, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Основные причины в C++:

  • Частые и разноразмерные вызовы new/delete или malloc/free.
  • Неоптимальная стратегия работы аллокатора стандартной библиотеки.
  • Длительная работа программы с "рваным" паттерном выделения/освобождения.

Прямой пример на C++:

int* block1 = new int[100]; // Выделен блок A
int* block2 = new int[200]; // Выделен блок B
int* block3 = new int[50];  // Выделен блок C

delete[] block1; // Освобождается блок A
// Теперь в куче есть свободный блок на 100 int, но он расположен между занятыми блоками B и C.
// Попытка выделить блок на 150 int может завершиться неудачей (std::bad_alloc),
// несмотря на то, что суммарно свободно 100 + 50 (если освободить C) = 150 элементов.
int* block4 = new int[150]; // Может вызвать ошибку из-за фрагментации.

Последствия:

  • Сбои выделения памяти (std::bad_alloc) при, казалось бы, достаточном свободном месте.
  • Увеличение времени на поиск подходящего блока аллокатором.
  • Рост общего потребления памяти (оверхеда) из-за служебных структур.

Способы борьбы в C++:

  1. Использование специализированных аллокаторов: std::pmr::memory_resource (C++17) с пулами (std::pmr::synchronized_pool_resource).
  2. Выделение памяти крупными "страницами" с последующим ручным управлением внутри приложения.
  3. Минимизация количества динамических выделений: использование контейнеров с reserve(), стековых объектов, std::make_shared (который часто выделяет блок для объекта и контрольного блока вместе).
  4. Использование компактных структур данных, избегающих указателей на множество мелких объектов.