Метод распределения памяти партнера - это алгоритм выделения памяти, который разделяет память на разделы, чтобы попытаться удовлетворить запрос памяти как можно более подходящим образом. Эта система использует разделение памяти на половинки, чтобы попытаться подобрать наилучшее соответствие. Согласно Дональду Кнуту, система друзей была изобретена в 1963 году Гарри Марковицем и впервые была описана Кеннетом К. Ноултоном (опубликовано в 1965 году). Распределение памяти Buddy относительно легко реализовать. Он поддерживает ограниченное, но эффективное разделение и объединение блоков памяти.
Существуют различные формы приятельской системы; те, в которых каждый блок разделен на два меньших блока, являются самой простой и наиболее распространенной разновидностью. Каждый блок памяти в этой системе имеет порядок, где порядок является целым числом от 0 до определенного верхнего предела. Размер блока порядка n пропорционален 2, так что размер блоков ровно в два раза превышает размер блоков, которые на один порядок меньше. Размеры блоков, равные степени двойки, упрощают вычисление адресов, поскольку все партнеры выровнены по границам адресов памяти, которые являются степенью двойки. Когда большой блок разделяется, он делится на два меньших блока, и каждый меньший блок становится уникальным другом для другого. Разделенный блок можно объединить только с его уникальным партнерским блоком, который затем преобразует более крупный блок, из которого они были отделены.
Вначале определяется размер наименьшего возможного блока, то есть наименьшего блока памяти, который может быть выделен. Если бы вообще не существовало нижнего предела (например, было бы возможно распределение размера в битах), для системы было бы много памяти и вычислительных затрат, чтобы отслеживать, какие части памяти выделены и нераспределены. Однако может быть желателен довольно низкий предел, чтобы свести к минимуму средние потери памяти на выделение (в отношении выделений, которые по размеру не кратны наименьшему блоку). Обычно нижний предел будет достаточно малым, чтобы свести к минимуму средний расход пространства на выделение, но достаточно большим, чтобы избежать чрезмерных накладных расходов. Затем наименьший размер блока принимается как размер блока нулевого порядка, так что все более высокие порядки выражаются как кратные степени двух этого размера.
Затем программист должен решить или написать код для получения максимально возможного порядка, который может поместиться в оставшемся доступном пространстве памяти. Поскольку общая доступная память в данной компьютерной системе может не быть кратной минимальному размеру блока в степени двойки, наибольший размер блока может не охватывать всю память системы. Например, если в системе было 2000 КБ физической памяти и размер блока нулевого порядка был 4 КБ, верхний предел порядка был бы 8, поскольку блок порядка 8 (256 блоков нулевого порядка, 1024 КБ) равен самый большой блок, который уместится в памяти. Следовательно, невозможно выделить всю физическую память в одном фрагменте; оставшиеся 976 КБ памяти должны быть выделены меньшими блоками.
Ниже приводится пример того, что происходит, когда программа запрашивает память. Допустим, в этой системе наименьший возможный блок имеет размер 64 килобайта, а верхний предел для порядка равен 4, что приводит к максимально возможному размещаемому блоку, 2 раза по 64 КБ = 1024 КБ. Ниже показано возможное состояние системы после различных запросов к памяти.
Шаг | 64 K | 64 K | 64 K | 64 K | 64 K | 64 К | 64 К | 64 К | 64 К | 64 К | 64 К | 64 К | 64 K | 64 K | 64 K | 64 K |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 2 | |||||||||||||||
2,1 | 2 | 2 | ||||||||||||||
2,2 | 2 | 2 | 2 | |||||||||||||
2,3 | 2 | 2 | 2 | 2 | ||||||||||||
2,4 | 2 | 2 | 2 | 2 | 2 | |||||||||||
2,5 | A: 2 | 2 | 2 | 2 | 2 | |||||||||||
3 | A: 2 | 2 | B: 2 | 2 | 2 | |||||||||||
4 | A: 2 | C: 2 | B: 2 | 2 | 2 | |||||||||||
5.1 | A: 2 | C: 2 | B: 2 | 2 | 2 | 2 | ||||||||||
5.2 | A: 2 | C: 2 | B: 2 | D: 2 | 2 | 2 | ||||||||||
6 | A: 2 | C: 2 | 2 | D: 2 | 2 | 2 | ||||||||||
7.1 | A : 2 | C: 2 | 2 | 2 | 2 | 2 | ||||||||||
7.2 | A: 2 | C: 2 | 2 | 2 | 2 | |||||||||||
8 | 2 | C: 2 | 2 | 2 | 2 | |||||||||||
9,1 | 2 | 2 | 2 | 2 | 2 | |||||||||||
9,2 | 2 | 2 | 2 | 2 | ||||||||||||
9,3 | 2 | 2 | 2 | |||||||||||||
9,4 | 2 | 2 | ||||||||||||||
9,5 | 2 |
Это распределение могло произойти следующим образом:
Как вы можете видеть, что происходит, когда запрос памяти делается следующим образом:
По сравнению с другими более простыми методами, такими как динамическое выделение, система партнерской памяти имеет небольшую внешнюю фрагментацию и допускает сжатие памяти с небольшими накладными расходами. Партнерский метод освобождения памяти является быстрым, при этом максимальное количество требуемых уплотнений равно log 2 (высший порядок). Обычно система распределения вспомогательной памяти реализуется с использованием двоичного дерева для представления используемых или неиспользуемых разделенных блоков памяти. «Друг» каждого блока может быть найден с помощью исключающего ИЛИ адреса блока и его размера.
Однако по-прежнему существует проблема внутренней фрагментации - память тратится впустую, потому что запрошенная память немного больше, чем маленький блок, но намного меньше, чем большой блок. Из-за того, как работает метод распределения памяти напарника, программе, запрашивающей 66 КБ памяти, будет выделено 128 КБ, что приведет к потере 62 КБ памяти. Эта проблема может быть решена с помощью выделения блоков, которое может быть наложено поверх более грубого вспомогательного распределителя, чтобы обеспечить более детальное распределение.
Одна версия алгоритма распределения партнеров была подробно описана Дональдом Кнутом в томе 1 книги Искусство компьютерного программирования. Ядро Linux также использует систему приятелей с дальнейшими модификациями для минимизации внешней фрагментации, а также различные другие распределители для управления памятью внутри блоков.
jemalloc
- это современный распределитель памяти, который использует, среди прочего, приятельская техника.
malloc (3)
Реализация для FreeBSD (PDF), стр. 4–5