最近遇到一个问题,为了提高查找速率,将一个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对象中,除了对象的值以外,还要包括比如引用计数,对象类型,引用地址等值,同时也会涉及到对象本身的实现,比如字典的哈希表。所以使用到的内存大小是远远高于对象本身的大小的。这在今后的编程中需要警惕。
网友评论