Python: утечка памяти?

Запрос в интерпретаторе Python:

Python 2.7.3 (default, Apr 10 2012, 23:31:26) [MSC v.1500 32 bit (Intel)] on win
32
Type "help", "copyright", "credits" or "license" for more information.
>>> k = [i for i in xrange(9999999)]
>>> import sys
>>> sys.getsizeof(k)/1024/1024
38
>>>

А вот - посмотрите, сколько нужно от оперативной памяти:


Использование памяти после оператора del k:

И после gc.collect():

Почему список целых чисел с ожидаемым размером 38 МБ занимает 160 МБ?

UPD: На эту часть вопроса был дан ответ (почти сразу и несколько раз :))

Хорошо, вот еще одна загадка:

Python 2.7.3 (default, Apr 10 2012, 23:31:26) [MSC v.1500 32 bit (Intel)] on win
32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys

>>> str = 'abcdefg'
>>> sys.getsizeof(str)
28
>>> k = []
>>> for i in xrange(9999999):
...     k.append(str)
...
>>> sys.getsizeof(str)*9999999/1024/1024
267

Как вы думаете, сколько он будет потреблять сейчас?


(источник: i.imm.io)

Размер str составляет 28, против 12 в прошлом примере. Таким образом, ожидаемое использование памяти составляет 267 МБ - даже больше, чем с целыми числами. Но это займет всего ~ 40 Мб!

Ответ 1

sys.getsizeof() не очень полезен, потому что он часто учитывает только часть того, что вы ожидаете. В этом случае он учитывает список, но не все целые объекты, которые находятся в списке. Список занимает примерно 4 байта на элемент. Целые объекты берут по 12 байт каждый. Например, если вы попробуете это:

k = [42] * 9999999
print sys.getsizeof(k)

вы увидите, что список по-прежнему занимает 4 байта на элемент, т.е. около 40 МБ, но поскольку все элементы являются указателями на один и тот же целочисленный объект 42, общее использование памяти не превышает 40 МБ.

Ответ 2

Что такое getizeof()

Сначала я предлагаю взглянуть на то, что означает размер оператора. Вы можете найти точное описание в документации. Я хочу увеличить масштаб при следующем предложении.

Учитывается только потребление памяти, непосредственно связанное с объектом, а не потребление памяти объектов, на которые он ссылается.

Это означает, что когда вы запрашиваете sys.getsizeof([a]), вы не получаете фактический размер массива. Вы получаете только размер всей памяти, предназначенной для управления списком. Список по-прежнему содержит целые числа 9999999. Каждое целое число состоит из 12 байтов, что приводит к сумме 114 МБ. Сумма памяти, предназначенная для управления массивом 32 МБ плюс сумма памяти данных в массиве, составляет 146 МБ, что намного ближе к вашему результату.