CPython использует собственный распределитель памяти для небольших объектов, pymalloc-allocator .Неплохое описание можно найти в самом коде .
Этот распределитель довольно хорош во избежании фрагментации памяти, поскольку он эффективно использует освобожденную память.Однако это всего лишь эвристика, и можно было бы придумать сценарии, которые приводят к фрагментации памяти.
Давайте разберемся, что происходит, когда мы выделяем объект размером 1 байт.
CPython имеет свойсобственная так называемая арена для объектов размером менее 512 байт.Очевидно, что 1-байтовый запрос будет управляться его распределителем.
Запрошенные размеры разделены на 64 различных класса: 0-й класс предназначен для размеров 1,8 байта, 1-й класс предназначен для размеров или 9 ..16 байтов и т. Д. - это связано с необходимостью выравнивания 8 байтов.Каждый из вышеперечисленных классов имеет свою более или менее независимую / выделенную память.Наш запрос для 0-го класса.
Давайте предположим, что это первый запрос для этого класса размера.Будет создан новый «пул» или будет использован пустой пул.Пул размером 4 КБ и, следовательно, имеет место для 512 8-байтовых «блоков».Несмотря на запрос только 1 байта, мы будем блокировать еще 7 байтов занятого блока, поэтому они не могут быть использованы для других объектов.Все свободные блоки хранятся в списке - в начале все 512 блоков находятся в этом списке.Распределитель удаляет первый блок из этого списка свободных блоков и возвращает его адрес в качестве указателя.
Сам пул помечен как «используемый» и добавлен в список используемых пулов для 0-го класса.
Теперь выделение другого объекта размером <= 8 байт происходит следующим образом.Сначала мы рассмотрим список используемых пулов для 0-го класса и найдем пул, который уже используется, то есть имеет несколько используемых и несколько свободных блоков.Allocator использует первый свободный блок, удаляет его из списка свободных блоков и возвращает его адрес в качестве указателя. </p>
Удалить первый объект легко - мы добавляем занятый блок в качестве заголовка списка свободных блоков в (пока один) используемый пул.
Когда создается новый объект размером 8 байт, поэтому используется первый блок в списке свободных блоков, и это блок, который использовался первым, теперьудален, объект.
Как видите, память используется повторно, и, таким образом, фрагментация памяти значительно уменьшается.Это не означает, что не может быть фрагментации памяти:
После выделения 512 однобайтовых объектов первый пул становится «полным», и будет создан / использован новый пул для размеров 0-го класса.Как только мы добавим еще 512 объектов, второй пул станет «полным».И так далее.
Теперь, если удаляются первые 511 элементов - будет еще один байт, блокирующий целые 4 КБ, которые нельзя использовать для других классов.
Только после освобождения последнего блока пул становится «пустым» и, следовательно, может использоваться повторно для других классов размеров.
Пустые пулы не возвращаются в ОС,но оставайтесь на арене и используйте их повторно.Однако pymalloc управляет несколькими аренами , и если арена становится «неиспользуемой», она может быть освобождена и занятая память (т. Е. Пулы) возвращается в ОС.