Раскрыт принцип работы пула памяти

алгоритм программист Тенсент C++

Приветствую всех вОблако Tencent + сообщество, получить больше крупной технической практики Tencent по галантерее ~

Эта статья написана [amc](cloud.Tencent.com/developer/U…)Опубликован вКолонка «Облако + сообщество»

В технологии динамической памяти приложений языка Си по сравнению сalloc/freeСистемный вызов, пул памяти (memory pool) — это технология, которая запрашивает большой кусок непрерывного пространства памяти в текущей системе, а затем выделяет его в соответствии с фактическими потребностями во время выполнения. Преимущества использования пулов памяти:

  1. намного быстрее, чемmalloc/freeБыстро, потому что количество системных вызовов уменьшается, особенно в случае частого выделения/освобождения блоков памяти
  2. Избегайте значительной фрагментации памяти системы после частого использования/освобождения памяти.
  3. сэкономить место

Классификация

По размеру выделенной памяти пулы памяти можно разделить на две категории:

Fixed-size Allocation

Блок памяти, выделяемый каждый раз (называемыйunitилиcell) — это значение, предварительно заданное программой. Когда блок памяти освобождается, необходимо просто повесить его обратно в связанный список пула памяти. Также известен как «буферный пул фиксированного размера».

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

Variable-size allocation

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

Использование этой области управления требует разумного планирования каждой памяти, поэтому ее также называют управлением памятью на основе области. Распределители, которые используют этот подход, например,Apache Portable Runtimeсерединаapr_poolинструмент. В этой статье такие пулы памяти не обсуждаются.


Принцип и структура

Концепции и структуры данных

Пул памяти фиксированной длины имеет некоторые основные и необходимые концепции, которые необходимо определить в данных структуры пула памяти. В следующей номенклатуре используется вариант венгерской записи, например.nNext,nУказывает, что тип переменной целочисленный. Так же,pПредставляет указатель.

Memory Unit

каждый вызов программыMemPool_AllocПосле получения области памяти будет получена непрерывная область памяти. Единица, управляющая одной такой областью памяти, называется единицей памяти, иногда именуемойchunk. Каждый блок должен содержать следующие данные:

  1. nNext: Целочисленные данные, представляющие идентификационный номер следующего выделенного устройства. Для функций, пожалуйста, обратитесь к следующему вопросу
  2. pData[]: Фактическая область памяти, размер которой указывается вызывающей стороной во время создания.

Memory Block

Блок памяти, содержащий ряд ячеек памяти.

Эта структура данных должна содержать следующую основную информацию:

  1. nSize: Целочисленные данные, указывающие размер блока в памяти.
  2. nFree: Целое число, указывающее, сколько единиц осталось нераспределенными.
  3. nFirst: Целое число, представляющее идентификационный номер следующего выделенного устройства.
  4. pNext: указатель на следующий блок памяти

Memory Pool

Общая структура данных управления пулом памяти, другими словами, объект пула памяти.

  1. pBlock: указатель на первый блок памяти
  2. nUnitSize: Целое число, представляющее размер каждой единицы
  3. nInitSize: Целое число, указывающее количество единиц в первом блоке.
  4. nGrowSize: Целое число, указывающее количество единиц каждого блока, которое продолжает увеличиваться за пределами первого блока.

функциональный интерфейс

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

memPoolCreate()

Создайте пул памяти, обязательным параметром является размер блока, а необязательным параметром является указанный выше пул памяти.nInitSizeиnGrowSize.

memPoolDestroy()

Уничтожьте весь пул памяти и верните его операционной системе.

memPoolAlloc()

Выделите модуль из пула памяти, размер которого является предопределенным размером модуля.

memPoolFree()

Освобождает указанный юнит.


процесс работы

Теперь давайте возьмем пул памяти с размером блока 1024 и размером инициализации 4 (каждый блок имеет 4 блока) в качестве примера, чтобы объяснить, как работает пул памяти. Далее предполагается, что ширина целого числа составляет 4 байта.

создать пул памяти

Программа запускается, вызывает и создает пул памяти. Функция, вызываемая в это время,memPoolCreate(), программа создаст структуру данных, соответствующие члены структуры и их значения следующие:

img

memory pool alloc

Когда звонящий впервые запрашиваетmemPoolAlloc()Когда пул памяти обнаруживает, что список блоков пуст, он хочет, чтобы система подала заявку на получение памяти, создала блок памяти и инициализировала его следующим образом (где значение адреса является предполагаемым значением):

img

вnSize = 4112 = sizeof(memPool) + nInitSize * sizeof(memUnit). КаждыйnNextДобавьте один по очереди, каждый из которых относится к следующему блоку, который следует сам за собой. последней единицыnNextЗначение не имеет смысла, поэтому его значение не указано.

Затем вернуть память в нужном блоке. Логика возврата памяти следующая:

  1. Пул памяти запрашивается в блокеnFreeчлен
  2. так какnFree > 0, указывая на то, что есть нераспределенный блок, поэтому продолжайте просмотр в этом блокеnFirstчлен
  3. nFirstРавно 0, что указывает на то, что модуль в позиции 0 в блоке доступен. Таким образом, пул памяти может преобразоватьpDataАдрес возвращается вызывающему абоненту.pDataЗначение адреса рассчитывается как:pBlock + sizeof(memBlock) + nFirst * (sizeof(memUnit)) + sizeof(nNext) = 0x10010
  4. nFreeминус один
  5. ИсправлятьnFirstзначение, отмечая следующую доступную единицу. Обратите внимание здесьnFirstВы не можете просто добавить единицу, но взять единицу, соответствующую единице, возвращенной вызывающему абоненту.nNextзначение, то есть следующий рисунок(2)по первоначальной стоимости1
  6. будетpDataЗначение адреса возвращается. Для иллюстрации мы обозначаем эту область как CA

Состояние каждой структуры данных после операции следующее:

img

второй звонокallocСитуация аналогичная. Статус каждой структуры данных после вызова следующий:

img

memory pool free

Давайте сначала посмотрим на результаты:

img

  1. Сначала программа проверит значение адреса ЦС, и вскоре обнаружится, что адрес 0x10010 находится в пределах диапазона первого блока выше (0x10000 <= 0x10010 <= (0x10000 + 4112)). Пересчет значения смещения может быстро получить соответствующийnNextэтикетка, которая на картинке выше(2)Место расположения.
  2. Рециркулирующее устройство, указывающее соответствующее значение элемента для указания статуса RECLAIM of Unit. Первая проверкаnFirst, см. предыдущий рисунок,nFirstимеет значение 3, указывающее местоположение(3)единица в наличии. Поэтому сначала ставим(2)гдеnNextУстановите значение 3, добавив его обратно в связанный список доступных единиц.
  3. будетnFirstЗначение изменяется на0, то есть метка, представляющая только что восстановленный блок, и(2)Величине at присваивается значение 2, что означает b(3)Единица

На самом деле, вы можете видеть, что это простая операция со связанным списком. Согласно приведенному выше процессу, если CB также будет освобожден, состояние пула памяти станет следующим:

img

К этому времени, поскольку весь блок полностью восстановлен (nFree == nInitSize), то по разным стратегиям можно рассматривать освобождение всего блока из памяти.

блок полный

мы возвращаемсяallocВ логике вы можете видеть, что пул памяти сначала проверяет блокnFreeчлен. еслиnFree == 0, то он будет в блокеpNextИди найди следующий блок, иди проверьnFree. Если обнаруживается, что список блоков закончился, это означает, что все текущие блоки заполнены и необходимо создать новый блок.

В реальном дизайне нам нужно подумать о выборе подходящего размера инициализации и размера увеличения. Как видно из приведенного выше алгоритма, еслиalloc/freeПри очень частом звонке эффективность использования первого блока очень высока.


варианты или улучшения

  1. В некоторых упрощенных версиях нет необходимости использоватьpNextЧтобы поддерживать список, который является только блоком, и использование памяти имеет четкий и контролируемый верхний предел. Часто используется при отсутствииmallocОСРВ для системных вызовов или некоторых встроенных систем, которые очень чувствительны к памяти.
  2. Если он будет использоваться в многопоточной среде, то структура пула памяти должна быть заблокирована.

использованная литература

Связанное Чтение [Ежедневная рекомендация курса] Машинное обучение в действии! Быстрый старт бизнеса в сфере онлайн-рекламы и знание CTR

Эта статья была разрешена автором для публикации в сообществе Tencent Cloud + Для получения дополнительных оригинальных текстов, пожалуйстанажмите

Найдите и подпишитесь на общедоступную учетную запись «Сообщество Yunjia», получите технические галантереи как можно скорее и ответьте на 1024 после подписки, чтобы отправить вам подарочный пакет технических курсов!

Огромный технический практический опыт, все вСообщество Юнцзя!