Как определить размер объекта в памяти в Python
Что проверяют
Знаете ли вы, что sys.getsizeof показывает только сам объект, без того, на что он ссылается. Следом просят посчитать полный размер вложенной структуры или найти, какая часть программы съедает память.
Как отвечать
Начните с sys.getsizeof(obj): размер в байтах самого объекта плюс заголовок сборщика мусора, если объект им отслеживается. Для контейнера это структура и массив указателей, по 8 байт на элемент на 64-битной сборке, плюс запас, который список резервирует под будущие append. Сами элементы не учитываются: список из миллиона длинных строк по getsizeof весит около 8 МБ, хотя реально занимает в разы больше.
Дальше инструменты по задаче:
- глубокий размер: рекурсивный обход через
gc.get_referentsс множеством уже посчитанныхid, чтобы не учитывать общие объекты дважды. Готовое решениеasizeofиз пакетаpympler; - где растёт память:
tracemallocиз стандартной библиотеки, снимкиtake_snapshot()до и после и сравнение по строкам кода. Для профилирования целиком удобенmemray; - память процесса: RSS через
psutil, но это про весь процесс, а не про объект; - NumPy и pandas: у массива
arr.nbytes, у датафреймаdf.memory_usage(deep=True). Безdeep=Trueстроки в колонках типаobjectпосчитаны как указатели.
Для своих классов можно определить __sizeof__. Экономить память помогают __slots__: экземпляр без __dict__ заметно легче, и на миллионах объектов разница измеряется сотнями мегабайт.
Где спотыкаются
Выдают getsizeof за полный размер. Словарь из вложенных списков покажет пару сотен байт, и кандидат делает вывод, что структура лёгкая.
Складывают getsizeof всех элементов без учёта общих объектов. Маленькие целые от -5 до 256 и интернированные строки в CPython существуют в одном экземпляре, а наивная сумма считает их многократно.
Судят по RSS после удаления объекта. Аллокатор CPython держит освобождённые арены и не всегда сразу отдаёт память системе, поэтому график процесса не падает, хотя объект уже удалён.