为什么我的班级会花这么多内存?

时间:2016-11-01 09:30:11

标签: python python-2.7 memory

from guppy import hpy

hp = hpy()


class Demo(object):
    __slots__ = ('v0', 'v1')

    def __init__(self, v0, v1):
        self.v0 = v0
        self.v1 = v1


from array import array

value = 1.01
ar = array('f')
ar2 = array('f')
for i in range(5000000):
    ar.append(value + i)
    ar2.append(value + i * 0.1 + i * 0.01 + i * 0.001 + i * 0.0001 + i * 0.000001)
a = []
for i in range(5000000):
    vex = Demo(ar[i], ar[2])
    a.append(vex)
print "Heap at the end of the functionn", hp.heap()

这是输出:

Heap at the end of the functionn Partition of a set of 15063247 objects. Total       size = 650251664 bytes.

Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
0 5000000  33 320000000  49 320000000  49 __main__.Demo
1 10000108  66 240002592  37 560002592  86 float
2    368   0 42008896   6 602011488  93 list
3      2   0 40000112   6 642011600  99 array.array
4  28182   0  2214784   0 644226384  99 str
5  12741   0  1058448   0 645284832  99 tuple
6    189   0   669624   0 645954456  99 dict of module
7    371   0   588104   0 646542560  99 dict (no owner)
8    258   0   509232   0 647051792 100 dict of sip.wrappertype
9   3176   0   406528   0 647458320 100 types.CodeType

我想知道Demo课为什么要花这么多内存。因为Demo类只保留float的引用,所以它不会复制float值。

getSizeOf(Demo) # 984

50W的演示类可能只需花费内存:984*50W=40215176但现在花费320000000。 令人难以置信,为什么?

1 个答案:

答案 0 :(得分:3)

sys.getsizeof()不会递归到子对象中,您只获取了的大小,而不是实例的大小。每个实例占用64个字节,加上每个float对象24个字节(在OS X上,使用Python 2.7.12):

>>> d = Demo(1.0, 2.0)
>>> sys.getsizeof(d)
64
>>> sys.getsizeof(d.v0)
24
>>> sys.getsizeof(d) + sys.getsizeof(d.v0) + sys.getsizeof(d.v1)
112

每个插槽仅为实例对象中的指针保留内存;在我的机器上,每个指针8个字节。

Demo()个实例和数组之间存在几个差异:

  • 实例支持引用计数和弱引用的开销最小,并且包含指向其类的指针。数组直接存储值,没有任何开销。
  • 实例存储 Python 浮动。这些是完整的对象,包括引用计数和弱引用支持。数组将单精度浮点数存储为C值,而Python float对象模型 double 精度浮点数。因此,实例使用2 * 24字节(在我的Mac上)仅用于那些浮点数,而在数组中每个单精度'f'值仅使用4个字节。
  • 要跟踪500万个Demo个实例,您还需要创建一个list对象,其大小可以处理至少 500万个对象引用。 array直接存储C单精度浮点数。

hp.heap()输出仅计算实例占用空间,而不计算每行的引用float值,但总计匹配:

  • 500万次64字节是Demo个实例的320.000.000字节内存。
  • 1000万次24字节是float个实例的240.000.000字节内存,再加上其他地方引用的108个浮点数。

这两个组一起组成了堆上1500万个Python对象的大部分。

  • 为保存实例而创建的list对象包含500万个指针,即指向所有Demo实例的40.000.000字节,以及该对象的会计开销。堆上还有367个列表,由其他Python代码引用。
  • 每个500万个4字节浮点数的2 array个实例是40.000.000字节,每个数组开销加56个字节。

所以array对象非常高效来存储大量数值,因为它将这些值存储为原始C值。但是,缺点是Python必须 box 您尝试访问的每个值;所以访问ar[10]会返回一个Python float对象。