python对象内存消耗浅析

2018-05-06  本文已影响0人  高稚商de菌

最近遇到一个问题,为了提高查找速率,将一个mysql表的内容加载到内存中,竟然消耗了远高于表大小的内存,导致内存耗尽。猜测是python对象占用的内存大。为了验证这个问题,做了一些测试。

先介绍一个函数:sys.getsizeof可以返回在内存中占的大小,单位为byte。以字节(byte)为单位返回对象大小。
以下是摘录:

这个对象可以是任何类型的对象。 所以内置对象都能返回正确的结果 但不保证对第三方扩展有效,因为和具体实现相关。
getsizeof() 调用对象的 __sizeof__ 方法, 如果对象由垃圾收集器管理, 则会加上额外的垃圾收集器开销。

也就是说,getsizeof和__sizeof__的结果可能会有些差异。比如列表和字典类型,getsizeof结果会大于__sizeof__。

说明一下,python结构占用内存大小和操作系统,尤其是操作系统位数高度相关。以下测试使用的操作系统为64位ubuntu系统。

首先是布尔型型,可以看到布尔型占了24Byte。对于整型而言,可以看到普通整型为24Byte,长整型为28Byte。

>>> b = False
>>> print sys.getsizeof(b)
24
>>> import sys
# int
>>> i = 1
>>> print sys.getsizeof(i)
24
>>> i = 1L
>>> print sys.getsizeof(i)
28

字符串类型:空字符串占了37Byte,每多一个字符多1Byte。

# string
>>> s = ""
>>> print sys.getsizeof(s)
37
>>> s = "a"
>>> print sys.getsizeof(s)
38
>>> s = "ab"
>>> print sys.getsizeof(s)
39

列表类型,空列表为72byte,每多一项元素,多8byte。无论这个元素是本身占多少内存,都是多8Byte。因为,在list中,只是存放了这个元素的内存地址,在64位操作系统中,内存地址的大小为8Byte。元组类型也是也是类似的。

# list
>>> l = []
>>> print sys.getsizeof(l)
72
>>> l = [1,2,3]
>>> print sys.getsizeof(l)
96
>>> l = [1,2,[1,2,3]]
>>> print sys.getsizeof(l)
96

字典类型,空字典占用280Byte,而有有了元素的列表依然是280?! 这是因为字典本质上是hash数组。初始化一个字典的时候,会分配一个长度为8的数组。随着数组中的被使用(或者曾经被使用过,这被称为dummpy slot)的槽位的总数超过数组长度的2/3,则需调整数组的长度,即rehash。数组长度调整后的长度不小于活动槽数量的 4 倍,而当活动槽的数量非常大(大于50000)时,调整后长度应不小于活动槽数量的2倍。也就是说,只有rehash,调用sys.getsizeof的结果才会有变化。

# dict
>>> d = {}
>>> print sys.getsizeof(d)
280
>>> d["a"] = "a"
>>> print sys.getsizeof(d)
280
>>> d["b"] = "b"
>>> print sys.getsizeof(d)
280

python对象中,除了对象的值以外,还要包括比如引用计数,对象类型,引用地址等值,同时也会涉及到对象本身的实现,比如字典的哈希表。所以使用到的内存大小是远远高于对象本身的大小的。这在今后的编程中需要警惕。

上一篇下一篇

猜你喜欢

热点阅读