Введение
Python - интерпретируемый язык программирования, поэтому перед выполнением код транслируется в машиночитаемые инструкции - байт-код. Байт-код интерпретируется виртуальной машиной, определяемой реализацией языка, например, стандартной - CPython.
Python не взаимодействует с памятью - только с её виртуальным представлением. В процессе выполнения программы операционная система создаёт процесс и выделяет под него ресурсы. В отличие от С/С++ мы не можем управлять памятью из кучи напрямую, а делаем это посредством memory manager, который и обращается к памяти через Python/C API.
Организация доступной виртуальной памяти
Непосредственно с сырой памятью взаимодействует raw memory allocator. Поверх него работают аллокаторы, специфичные для отдельных типов объектов.
Python использует динамическую стратегию распределения памяти, то есть распределение памяти происходит во время выполнения программы.
Виртуальная память представляет собой иерархическую структуру, оптимизированную под блоки размером 256Кб.
Арена - 256Кб
Пул - 4Кб
Блок - от 16 до 512 байт
Блок
Содержит не более одного объекта и находится в одном из трёх состояний:
untouched
- блок ещё не использовалсяfree
- блок использовался механизмом памяти, но больше не содержит использованных программой данныхallocated
- блок хранит данные
Пул
Пул также имеет три состояния:
used
- занятfull
- заполненempty
- пуст (отused
отличаются отсутствиемallocated
блоков) Пулы одного типа и одного размера блоков организованы в двусвязные списки.
Арена
Хранит в себе пулы любых видов. Арены хранятся в двусвязном списке и отсортированы по количеству доступных пустых пулов.
Освобождение памяти: счётчик ссылок и сборщик мусора
Так в Python всё является объектом, то каждое существо имеет прародителя - это PyObject. В нём определены счётчики ссылок и указатель на фактический тип объекта. Это хорошо работает пока Python не сталкивается с циклическим созданием ссылок. Например, когда два объекта ссылаются друг на друга. Для борьбы с такими проблемами поднимается сборщик мусора.
Советы
Обращайте внимание на работу с неизменяемыми объектами. К примеру, вместо присваивания строк, используйте
.join()
или.format()
.Избегайте вложенных циклов. Это приводит к созданию чрезмерно большого количество объектов в виртуальной памяти процесса.
Используйте кэширование.
Профилируйте код.
Заключение
При использовании базовых возможностей Python знание о внутреннем устройстве память в интерпретаторе не обязательно, но когда дело доходит до широкомасштабных коммерческих проектов, то узнать тонкости для лучшей оптимизации кодовой базы всё же будет полезным.