Determine the Size of an Object in Python
- the sys Module in Python
- Use the getsizeof() Function in the sys Module to Get the Object Size in Python
There are different built-in data types in programming like numeric, text, sequence, and boolean. In Python, all these data types are considered objects. Every object needs some space in the memory to store itself. So, these objects store themselves in the memory and occupy space in the form of bytes . Every object has a different storage size, and this tutorial demonstrates how to find the size of an object in Python.
the sys Module in Python
The sys module of Python helps a user perform various operations and manipulations on various parts of the Python runtime environment by providing multiple functions and variables. One can easily interact with the interpreter through different variables and functions. While using the sys module, you can easily access system-specific functions and parameters.
The sys module is also used in determining the size of an object in Python.
Use the getsizeof() Function in the sys Module to Get the Object Size in Python
The getsizeof() function provided by the sys module is the most commonly used function to get the size of a particular object in Python. This function stores an object as its function argument, calls that object’s sizeof() function, and finally returns the output.
import sys s = sys.getsizeof("size of this string object") print(s) s = sys.getsizeof(1010101) print(s) s = sys.getsizeof(1: "size", 2: "of", 3: "this", 4: "dictionary">) print(s)
63 24 280
Note that the sizes of the given objects returned are in bytes.
Lakshay Kapoor is a final year B.Tech Computer Science student at Amity University Noida. He is familiar with programming languages and their real-world applications (Python/R/C++). Deeply interested in the area of Data Sciences and Machine Learning.
Related Article — Python Object
- Anonymous Objects in Python
- Add Attribute to Object in Python
- Check if a Python Object Has Attributes
- Fix Object Has No Attribute Error in Python
- Fix Error List Object Not Callable in Python
- Fix Object Is Not Subscriptable Error in Python
Использование памяти в Python
Меня часто донимали размышление о том, насколько эффективно Python использует память по сравнению с другими языками программирования. Например, сколько памяти нужно, чтобы работать с 1 миллионом целых чисел? А с тем же количеством строк произвольной длины?
Как оказалось, в Python есть возможность получить необходимую информацию прямо из интерактивной консоли, не обращаясь к исходному коду на C (хотя, для верности, мы туда все таки заглянем).
Удовлетворив любопытство, мы залезем внутрь типов данных и узнаем, на что именно расходуется память.
Все примеры были сделаны в CPython версии 2.7.4 на 32 битной машине. В конце приведена таблица для потребности в памяти на 64 битной машине.
Необходимые инструменты
sys.getsizeof и метод __sizeof__()
Первый инструмент, который нам потребуется находится в стандартной библиотеки sys. Цитируем официальную документацию:
sys.getsizeof(объект[, значение_по_умолчанию])
Возвращает размер объекта в байтах.
Если указано значение по умолчанию, то оно вернется, если объект не предоставляет способа получить размер. В противном случае возникнет исключение TypeError.
Getsizeof() вызывает метод объекта __sizeof__ и добавляет размер дополнительной информации, которая хранится для сборщика мусора, если он используется.
Алгоритм работы getsizeof(), переписанной на Python, мог бы выглядеть следующем образом:
Py_TPFLAGS_HAVE_GC = 1
Где PyGC_Head — элемент двойного связанного списка, который используется сборщиком мусора для обнаружения кольцевых ссылок. В исходном коде он представлен следующей структурой:
typedef union _gc_head < struct < union _gc_head *gc_next; union _gc_head *gc_sourcev; Py_ssize_t gc_refs; >gc; long double dummy; > PyGC_Head;
Размер PyGC_Head будет равен 12 байт на 32 битной и 24 байта на 64 битной машине.
Попробуем вызвать getsizeof() в консоли и посмотрим, что получится:
>>> import sys >>> GC_FLAG = 1 >> sys.getsizeof(1) 12 >>> (1).__sizeof__() 12 >>> bool(type(1).__flags__ & GC_FLAG) False >>> sys.getsizeof(1.1) 16 >>> (1.1).__sizeof__() 16 >>> bool(type(1.1).__flags__ & GC_FLAG) False >>> sys.getsizeof('') 21 >>> ''.__sizeof__() 21 >>> bool(type('').__flags__ & GC_FLAG) False >>> sys.getsizeof('hello') 26 >>> sys.getsizeof(tuple()) 24 >>> tuple().__sizeof__() 12 >>> bool(type(tuple()).__flags__ & GC_FLAG) True >>> sys.getsizeof(tuple((1, 2, 3))) 36
За исключением магии с проверкой флагов, все очень просто.
Как видно из примера, int и float занимают 12 и 16 байт соответственно. Str занимает 21 байт и еще по одному байту на каждый символ содержимого. Пустой кортеж занимает 12 байт, и дополнительно 4 байта на каждый элемент. Для простых типов данных (которые не содержат ссылок на другие объекты, и соответственно, не отслеживаются сборщиком мусора), значение sys.getsizeof равно значению, возвращаемого методом __sizeof__().
id() и ctypes.string_at
Теперь выясним, на что именно расходуется память.
Для этого нужно нам нужны две вещи: во-первых, узнать, где именно хранится объект, а во-вторых, получить прямой доступ на чтение из памяти. Несмотря на то, что Python тщательно оберегает нас от прямого обращения к памяти, это сделать все таки возможно. При этом нужно быть осторожным, так как это может привести к ошибке сегментирования.
Встроенная функция id() возвращает адрес памяти, где храниться начала объекта (сам объект является C структурой)
>>> obj = 1 >>> id(obj) 158020320
Чтобы считать данные по адресу памяти нужно воспользоваться функцией string_at из модуля ctypes. Ее официальное описание не очень подробное:
ctypes.string_at(адрес[, длина])
Это функция возвращает строку, с началом в ячейки памяти «адрес». Если «длина» не указана, то считается что строка zero-terminated,
Теперь попробуем считать данные по адресу, который вернул нам id():
>>> import ctypes >>> obj = 1 >>> sys.getsizeof(obj) 12 >>> ctypes.string_at(id(obj), 12) 'u\x01\x00\x00 \xf2&\x08\x01\x00\x00\x003\x01\x00\x00 \xf2&\x08\x00\x00\x00\x001\x00\x00\x00'
Вид шестнадцатеричного кода не очень впечатляет, но мы близки к истине.
Модель Struct
Для того чтобы представить вывод в значения, удобные для восприятия, воспользуемся еще одним модулем. Здесь нам поможет функция unpack() из модуля struct.
struct
Этот модуль производит преобразование между значениями Python и структурами на C, представленными в виде строк.
struct.unpack(формат, строка)
Разбирает строку в соответствие с данным форматов. Всегда возвращает кортеж, даже если строка содержит только один элемент. Строка должна содержать в точности то количество информации, как описано форматом.
Форматы данных, которые нам потребуются.
| символ | Значение C | Значение Python | Длина на 32битной машине |
| c | char | Строка из одного символа | 1 |
| i | int | int | 4 |
| l | long | int | 4 |
| L | unsigned long | int | 4 |
| d | double | float | 8 |
Теперь собираем все вместе и посмотрим на внутреннее устройство некоторых типов данных.
Int
>>> obj = 1 >>> sys.getsizeof(obj), obj.__sizeof__() (12, 12) >>> struct.unpack('LLl', ctypes.string_at(id(obj), 12)) (373, 136770080, 1)
О формате значений несложно догадаться.
Первое число (373) — количество указателей, на объект.
>>> obj2 = obj >>> struct.unpack('LLl', ctypes.string_at(id(obj), 12)) (374, 136770080, 1)
Как видно, число увеличилось на единицу, после того как мы создали еще одну ссылку на объект.
Второе число (136770080) — указатель (id) на тип объекта:
>>> type(obj) >>> id(type(obj) ) 136770080
Третье число (1) — непосредственно содержимое объекта.
>>> obj = 1234567 >>> struct.unpack('LLl', ctypes.string_at(id(obj), 12)) (1, 136770080, 1234567)
Наши догадки можно подтвердить, заглянув в исходный код CPython
typedef struct < PyObject_HEAD long ob_ival; >PyIntObject;
Здесь PyObject_HEAD — макрос, общий для всех встроенных объектов, а ob_ival — значение типа long. Макрос PyObject_HEAD добавляет счетчик количества указателей на объект и указатель на родительский тип объекта — как раз то, что мы и видели.
Float
Число с плавающей запятой очень похоже на int, но представлено в памяти C значением типа double.
typedef struct < PyObject_HEAD double ob_fval; >PyFloatObject;
В этом легко убедиться:
>>> obj = 1.1 >>> sys.getsizeof(obj), obj.__sizeof__() (16, 16) >>> struct.unpack('LLd', ctypes.string_at(id(obj), 16) (1, 136763968, 1.1)
Строка (Str)
Строка представлена в виде массива символов, оканчивающимся нулевым байтом. Также в структуре строки отдельного сохраняется ее длина, хэш от ее содержания и флаг, определяющий, хранится ли она во внутреннем кэше interned.
typedef struct < PyObject_VAR_HEAD long ob_shash; # хэш от строки int ob_sstate; # находится ли в кэше? char ob_sval[1]; # содержимое строки + нулевой байт >PyStringObject;
Макрос PyObject_VAR_HEAD включает в себя PyObject_HEAD и добавляет значение long ob_ival, в котором хранится длина строки.
>>> obj = 'hello world' >>> sys.getsizeof(obj), obj.__sizeof__() (32, 32) >>> struct.unpack('LLLli' + 'c' * (len(obj) + 1), ctypes.string_at(id(obj), 4*5 + len(obj) + 1)) (1, 136790112, 11, -1500746465, 0, 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\x00')
Четвертое значение соответствует хэшу от строки, в чем нетрудно убедиться.
>>> hash(obj) -1500746465
Как видно, значение sstate равно 0, так что строка сейчас не кэшируется. Попробуем ее добавить в кэш:
>>> intern(obj) 'hello world' >>> struct.unpack('LLLli' + 'c' * (len(obj) + 1), ctypes.string_at(id(obj), 4*5 + len(obj) + 1)) (2, 136790112, 11, -1500746465, 1, 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\x00')
Кортеж (Tuple)
Кортеж представлен в виде массива из указателей. Так как его использование может приводить к возникновению кольцевых ссылок, он отслеживается сборщиком мусора, на что расходуется дополнительная память (об этом нам напоминает вызов sys.getsizeof())
Структура tuple похоже на строку, только в ней отсутствуют специальные поля, кроме длины.
typedef struct < PyObject_VAR_HEAD PyObject *ob_item[1]; >PyTupleObject;
>>> obj = (1,2,3) >>> sys.getsizeof(obj), obj.__sizeof__() (36, 24) >>> struct.unpack('LLL'+'L'*len(obj), ctypes.string_at(id(obj), 12+4*len(obj))) (1, 136532800, 3, 146763112, 146763100, 146763088) >>> for i in obj: print i, id(i) 1 146763112 2 146763100 3 146763088
Как видим из примера, последние три элементы кортежа являются указателями на его содержимое.
Остальные базовые типы данных (unicode, list, dict, set, frozenset) можно исследовать аналогичным образом.
Что в итоге?
| Тип | Имя в CPython | формат | Формат, для вложенных объектов | Длина на 32bit | Длина на 64bit | Память для GC* |
| Int | PyIntObject | LLl | 12 | 24 | ||
| float | PyFloatObject | LLd | 16 | 24 | ||
| str | PyStringObject | LLLli+c*(длина+1) | 21+длина | 37+длина | ||
| unicode | PyUnicodeObject | LLLLlL | L*(длина+1) | 28+4*длина | 52+4*длина | |
| tuple | PyTupleObject | LLL+L*длина | 12+4*длина | 24+8*длина | Есть | |
| list | PyListObject | L*5 | L*длину | 20+4*длина | 40+8*длина | Есть |
| Set/ frozenset |
PySetObject | L*7+(lL)*8+lL | LL* длина | ( (>5 элементов) 100+8*длина | ( (>5 элементов) 200+16*длина | Есть |
| dict | PyDictObject | L*7+(lLL)*8 | lLL*длина | ( (>5 элементов) 124+12*длина | ( (>5 элементов) 248+24*длина | Есть |
* Добавляет 12 байт на 32 битной машине и 32 байта на 64 битной машине
Мы видим, что простые типы данных в Python в два-три раза больше своих прототипов на C. Разница обусловлена необходимостью хранить количество ссылок на объект и указатель на его тип (содержимое макроса PyObject_HEAD). Частично это компенсируется внутренним кэшированием, который позволяет повторно использовать ранее созданные объекты (это возможно только для неизменяемых типов).
Для строк и кортежей разница не такая значительная — добавляется некоторая постоянная величина.
А списки, словари и множества, как правило, занимают больше на 1/3, чем необходимо. Это обусловлено реализацией алгоритма добавления новых элементов, который приносит в жертву память ради экономии времени процессора.
Итак, отвечаем на вопрос в начале статьи: чтобы сохранить 1 миллион целых чисел нам потребуется 11.4 мегабайт (12*10^6 байт) на сами числа и дополнительно 3.8 мегабайт (12 + 4 + 4*10^6 байт) на кортеж, которых будет хранить на них ссылки.
UPD: Опечатки.
UPD: В подзаголовке «1 миллион целых чисел», вместо «1 миллион простых чисел»
- Python
- Программирование
Как узнать сколько места в памяти занимает матрица или массив?
Eсть ли какая-нибудь функция, которая позволяет узнать, сколько места в памяти занимает матрица или массив numpy?
Отслеживать
51.6k 200 200 золотых знаков 61 61 серебряный знак 242 242 бронзовых знака
задан 20 окт 2016 в 19:02
Толкачёв Иван Толкачёв Иван
609 1 1 золотой знак 11 11 серебряных знаков 25 25 бронзовых знаков
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Можно воспользоваться методом nbytes:
In [5]: a = np.random.rand(10**6) In [6]: a.nbytes Out[6]: 8000000 In [7]: a.dtype Out[7]: dtype('float64')
это работает и для многомерных матриц:
In [11]: a = np.random.randint(0, 10, (10**3, 10**3, 10**3), dtype=np.int8) In [12]: a.shape Out[12]: (1000, 1000, 1000) In [13]: a.nbytes Out[13]: 1000000000 In [14]: a.dtype Out[14]: dtype('int8')
также можно воспользоваться стандартной функцией: sys.getsizeof()
In [33]: a = np.random.rand(10**6) In [34]: sys.getsizeof(a) Out[34]: 8000096
которая вызовет __sizeof__ для вызываемого объекта:
In [35]: a.__sizeof__() Out[35]: 8000096
getsizeof() calls the object’s __ sizeof __ method and adds an additional garbage collector overhead if the object is managed by the garbage collector.
Отслеживать
ответ дан 20 окт 2016 в 19:07
MaxU - stand with Ukraine MaxU - stand with Ukraine
149k 12 12 золотых знаков 59 59 серебряных знаков 132 132 бронзовых знака
В простом случае, numpy.array можно рассматривать как кусок памяти с соответствующими метаданными (тип, размеры, где/что лежит). .nbytes возвращает размер куска памяти (место занимаемое элементами массива—в идеализированной модели: возможно игнорируя выравнивание, вспомогательные структуры), не учитывая метаданные. sys.getsizeof() обещает вернуть размер Питон-объекта ( numpy.array() ) в байтах, что может включать в себя место и под метаданные (иногда мусор возвращается). Эти цифры следует рассматривать как оценки необходимой памяти, а фактическую память можно узнать измерениями.
21 окт 2016 в 6:31
- python
- numpy
Распределение памяти в Python: сколько и в каких случаях занимают типы данных
Как устроено выделение памяти под объекты в Python, как работает очистка памяти и в чём разница в памяти на примере типов list, dict и tuple.
Идея статьи возникла после просмотра одного видео, где автор разбирает различные способы создания списка из одинаковых элементов. Меня заинтересовала эта тема, и я начал углубляться в нее. В частности, почему в том или ином случае объем занимаемой памяти отличается.
В этом материале разберем, как устроено выделение памяти под объекты в Python. Потом кратко о том, как работает очистка памяти от неиспользуемых объектов. И, наконец, о разнице в занимаемой памяти на примере типов list, dict и tuple.
Выделение памяти
Напрямую из кода память не выделяется. Вся работа по выделению памяти перекладывается на менеджеров памяти. Есть один общий менеджер «верхнего» уровня, который отвечает за выделение большого блока из выделенной программе памяти — «арена». Занимает 256Кб.
Далее арена делится на «пулы» по 4Кб. Каждый пул может содержать в себе только блоки заранее определенного для этого пула размера — от 8 байт до 512 байт. Арена может содержать в себе пулы разных размеров, а вот блоки в одном пуле всегда одного размера. И вот на уровне блоков работают менеджеры памяти каждого конкретного типа данных.
Когда менеджер определенного типа запрашивает память для объекта, он заранее знает размер и может сразу обратиться к пулу с нужным размером блоков, разместив объект в первом свободном блоке. Если же свободных блоков нет или же пулов нужного размера нет, верхний менеджер выдает новый пул из наиболее заполненной арены. Если и все арены заняты, запрашивается новая арена.
Интересно, что частично вернуть выделенную под арену память нельзя, пока в ней есть хоть один непустой пул, в котором есть хоть один непустой блок. Как блоки становятся пустыми, мы обсудим в следующем разделе.
Освобождение памяти
В Python нет необходимости в ручной очистке памяти. Если объект больше не используется, все это перекладывается на сам интерпретатор и два механизма: счетчик ссылок и сборщик мусора.
Когда переменной присваивается значение, на самом деле сначала создается объект в подходящем свободном блоке (как работает выделение памяти разобрались в прошлом разделе) и потом уже в переменную кладется ссылка на этот объект.
Счетчик ссылок
Каждый созданный объект имеет специальное поле — счетчик ссылок. Он хранит в себе количество ссылающихся на него объектов. Увеличивает свое значение, например, когда используется операция присваивания, или когда объект становится частью списка. При удалении переменной или же при использовании del счетчик ссылок уменьшается на 1. Например, при завершении работы функции, где эта переменная была объявлена.
Разберем на примере небольшой кусок кода:
a = 1000 b = a print(sys.getrefcount(1000)) # 5 del b print(sys.getrefcount(1000)) # 4
В данном случае 1000 — это неизменяемый объект, один на всю программу. После инициализации двух переменных счетчик ссылок равен 5.
Почему 5? Потому что на объект 1000 ссылаются не только эти две переменные, а все переменные со значением 1000 во всех используемых модулях. Далее удаляем переменную b и счетчик ссылок меняет свое значение на 4. Как только счетчик достигает 0, объект освобождает блок.
Сборщик мусора
У счетчика ссылок есть большой недостаток — невозможность отловить циклические ссылки. Если объект или несколько объектов ссылаются друг на друга, счетчик никогда не опустится ниже 1.
Специально для обработки таких случаев был создан модуль gc. Его работа заключается в том, чтобы периодически сканировать объекты контейнерного типа и определять наличие циклических ссылок.
Говоря о сборщике мусора, важным понятием является поколение объектов — это набор объектов, за которыми следит, а в последующем сканирует сборщик. Есть три поколения, каждое из которых сканируется с разной частотой. Чаще сканируются объекты первого поколения, т.к. туда попадают новые объекты. Как правило, такие объекты имеют маленький срок жизни и являются временными. Поэтому целесообразно их проверять больше. Если объекты прошли сканирование сборщика мусора и не были удалены, то переходят в следующее поколение.
Сканирование первого поколения начинается, когда количество созданных объектов контейнерного типа превышает количество удаленных на заданный порог. Например, сканирование второго поколения начнется, когда количество сканирований первого поколения превысит заданный порог. По умолчанию, пороги срабатывания — это 700, 10 и 10, соответственно.
Посмотреть их можно через gc.get_threshold(). Изменить через gc.set_threshold().
print(gc.get_threshold()) # (700, 10, 10)
Все это актуально для list, dict, tuple и еще для классов. Не для простых типов.
Что там по типам данных
List
Начнем сравнения с list. Поскольку он изменяемый и будет интересно посмотреть, как он ведет себя при изменении количества элементов. Список представляет из себя массив не фиксированного размера, с возможностью произвольной вставки или удаления любого элемента. Элементом списка может быть любой тип данных. С точки зрения хранения списка в памяти, он состоит из двух блоков. Один блок фиксированного размера и хранит информацию о списке. Другой же хранит ссылки на элементы и может переходить из блока в блок, если количество элементов меняется.
Размер занимаемого объектом блока памяти можно посмотреть через sys.getsizeof().
empty_list = [] print(sys.getsizeof(empty_list)) # 64
Как мы видим, пустой объект списка уже занимает 64 байта.
a = [1, 2, 3] print(sys.getsizeof(a)) # 88
При обычном создании списка через перечисление элементов он всегда будет занимать: 64 байта пустой список + 8 байт на каждый элемент, т.к. список представляет из себя массив ссылок на объекты.
b = [i for i in range(3)] print(sys.getsizeof(b)) # 96
При создании через list comprehension размер будет уже 96. Что больше, чем размер пустого списка + 8 байт на каждый элемент. Работа этого механизма сводится к вызову метода append у создаваемого объекта списка. append работает следующим образом: в зависимости от уже присутствующего в списке количества элементов он заранее резервирует больше памяти при добавлении элемента. Дополнительно выделяемый объем памяти не всегда увеличивает список вдвое. Может быть выделено место всего под несколько элементов. Например, если к списку из 8-ми элементов добавляют еще один, будет зарезервировано место еще под восемь элементов. А при добавлении к списку из 16-ти элементов будет зарезервировано место всего под 9 элементов. Это позволяет избежать затрат на изменение размера списка при частых вызовах append. При этом неиспользуемая, но уже выделенная, память недоступна для чтения.
c_tuple = (1, 2, 3) c = list(c_tuple) print(sys.getsizeof(c)) # 112
При создании списка на основе кортежа получившийся список будет занимать 112 байт. В данном случае заранее резервируется место под еще 3 элемента списка, помимо уже присутствующих в кортеже.
Итого получаем 64 байта, + 8 * 3 — это элементы из кортежа, + 8 * 3 зарезервированное место под новые элементы.
Tuple
Кортеж представляет из себя массив фиксированной длины, заданной при создании объекта. Элементами кортежа также могут быть объекты любых типов. В отличие от списка, кортеж в памяти представлен одним объектом. Поскольку нет изменяемой части, которую надо перемещать между блоками. Да, и методов для изменения элементов у кортежа так же нет. Но если сам элемент принадлежит к изменяемому типу, его все же можно изменить.
empty_tuple = () print(sys.getsizeof(empty_tuple)) # 48
Пустой кортеж занимает блок из 48 байт. Помним, что кортежи неизменяемы. Если в памяти уже есть такой же кортеж, то новый объект создан не будет. Вместо этого переменной присваивается ссылка на существующий объект.
empty_tuple = () empty_tuple_2 = () print(id(empty_tuple) == id(empty_tuple_2)) # True
На примере видно, что адреса в памяти у двух кортежей совпадают. Аналогичное сравнение для списков вернуло бы False.
a = (1, 2, 3, 4) print(sys.getsizeof(a)) # 80
В случае непустого кортежа с памятью так же все просто. Объем блока будет равен 48 байтам + по 8 байт на каждый элемент.
b_list = [1, 2, 3, 4] b = tuple(b_list) print(sys.getsizeof(b)) # 80
С созданием кортежа из списка или другой коллекции тоже нет таких неожиданностей, как со списком. Т.к. не надо закладывать место под изменение количества элементов.
Dict
Словарь представляет из себя массив ключей и массив значений, где каждый ключ связан с одним значением. На ключ накладывается ограничение по уникальности в пределах словаря. Поэтому ключами могут быть объекты только неизменяемых типов. Значением же может быть объект любого типа.
Как и списки, словари хранятся в виде двух объектов. Первый, содержит информацию о самом словаре и всегда остается в одном и том же блоке. Второй, хранит пары ключ-значение и может перемещаться между блоками при изменении размера. Но при этом пустой словарь занимает гораздо больше места.
empty_dict = <> print(sys.getsizeof(empty_dict)) # 240
Как видно в примере, словарь занимает 240 байт. При создании словаря выделяется место под несколько элементов, а не только после добавления элемента, как это было со списком.
Если вызвать метод clear, очищаются не только все элементы. Изначально зарезервированная память тоже будет освобождена.
empty_dict = <> print(sys.getsizeof(empty_dict)) # 240 empty_dict.clear() print(sys.getsizeof(empty_dict)) # 72
В итоге словарь стал занимать всего 72 байта. Гораздо меньше, чем при создании.
Как и со списком, на каждую новую пару ключ-значение весь объект не перемещается в новый блок. Чтобы избежать частых затрат на перемещение, новый блок берется с запасом на несколько элементов.
Заключение
Знать, какой объем памяти будет занимать тот или иной объект, полезно. Например, в условиях проекта или задачи указаны жесткие ограничения по памяти. Или кусок кода вызывается очень часто, необходимо понять, не будет ли выделение/освобождение памяти являться узким местом вашей системы.
Поэтому рекомендую, прикидывать примерные затраты памяти, еще на момент написания кода. И никто не отменяет переиспользование уже существующих объектов.
Если вы заинтересовались этой темой и решили углубиться в нее, то обратите внимание на следующие материалы:
— Memory management in Python. Тут подробно описан механизм работы менеджеров памяти и организации блоков.
— Memory Management. Не забываем про официальную документацию.
— Garbage Collection for Python. Здесь детальнее рассказывают про алгоритм работы сборщика мусора.
— The Garbage Collector. И еще один материал о сборщике мусора.
