【问题标题】:Why does my class cost so much memory?为什么我的课要消耗这么多内存?
【发布时间】:2016-11-01 09:30:11
【问题描述】:
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 类只保留了对浮点数的引用,所以它不会复制浮点值。

getSizeOf(Demo) # 984

50W 的 Demo 类可能只消耗内存:984*50W=40215176,但现在需要消耗 320000000。 难以置信,为什么?

【问题讨论】:

  • 984 乘以 5000 万是 4920000000,而不是 40215176。

标签: python python-2.7 memory


【解决方案1】:

sys.getsizeof() 不会递归到子对象中,并且您只获取了 class 的大小,而不是实例的大小。每个实例占用 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 代码引用。
  • 2 array 实例,每 500 万个 4 字节浮点数为 40.000.000 字节,加上每个数组开销 56 字节。

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

【讨论】:

    猜你喜欢
    • 2019-09-30
    • 2018-10-10
    • 1970-01-01
    • 2010-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-12
    • 1970-01-01
    相关资源
    最近更新 更多