为什么我的python 2.7进程使用了越来越多的内存



考虑到列表只有76MB长,我正试图理解为什么这个python代码会导致一个需要236MB内存的进程。

import sys
import psutil
initial = psutil.virtual_memory().available / 1024 / 1024
available_memory = psutil.virtual_memory().available
vector_memory = sys.getsizeof([])
vector_position_memory = sys.getsizeof([1]) - vector_memory
positions = 10000000
print "vector with %d positions should use %d MB of memory " % (positions, (vector_memory + positions * vector_position_memory) / 1024 / 1024)
print "it used %d MB of memory " % (sys.getsizeof(range(0, positions)) / 1024 / 1024)
final = psutil.virtual_memory().available / 1024 / 1024
print "however, this process used in total %d MB" % (initial - final)

输出为:

vector with 10000000 positions should use 76 MB of memory 
it used 76 MB of memory 
however, this process used in total 236 MB

再增加x10个位置(即positions = 100000000(会产生x10个内存。

vector with 100000000 positions should use 762 MB of memory 
it used 762 MB of memory 
however, this process used in total 2330 MB

我的最终目标是尽可能多地吸取记忆,创建一个很长的列表。为了做到这一点,我创建了这个代码来理解/预测基于可用内存的列表有多大。令我惊讶的是,python需要大量的内存来管理我的列表。

为什么python使用这么多内存?!它在用它做什么?我知道如何预测python的内存需求,以有效地创建一个列表来使用几乎所有可用的内存,同时防止操作系统进行交换吗?

getsizeof函数只包括列表本身使用的空间。

但该列表实际上只是指向int对象的指针数组,您创建了10000000个指针,每个指针也占用内存,通常为24字节。

前几个数字(通常最多255个(是由解释器预先创建和缓存的,因此它们实际上是免费的,但其余的则不是。所以,你想添加这样的东西:

int_memory = sys.getsizeof(10000)
print "%d int objects should use another %d MB of memory " % (positions - 256, (positions - 256) * int_memory / 1024 / 1024)

然后结果会更有意义。


但请注意,如果您不是创建具有10M个唯一int的range,而是创建0-10000中的10M个随机int,或0的10M副本,则该计算将不再正确。因此,如果想处理这些情况,您需要做一些事情,比如将迄今为止看到的每个对象的id隐藏起来,并跳过对同一id的任何额外引用。

Python2.x文档曾经有一个链接到一个旧的递归getsizeof函数,这个函数可以实现这一点,还有更多……但这个链接已经失效,所以它被删除了。

3.x文档有一个指向新文档的链接,该链接可能在Python2.7中工作,也可能不工作。(我很快就注意到,它对print使用了__future__语句,并从reprlib.repr回退到repr,所以它可能会这样做。(


如果你想知道为什么每个int都是24字节长(在64位CPython中;当然,不同的平台和实现不同(:

CPython将每个内置类型表示为C结构,该结构至少包含refcount和指向该类型的指针的空间。对象需要表示的任何实际值都是附加值1因此,最小的非单例类型每个实例需要24个字节。


如果你想知道如何避免每个整数最多使用24个字节,答案是使用NumPy的ndarray——或者,如果出于某种原因不能使用,则使用stdlib的array.array

任何一种都可以指定"本机类型",如NumPy的np.int32array.arrayi,并创建一个直接包含100M这些本机类型值的数组。这将需要每个值正好4个字节,加上几十个恒定字节的标头开销,这比list的8个字节的指针小得多,再加上结尾的一点随长度而变化的松弛,加上包裹每个值的int对象。

使用array.array,你牺牲了速度来换取空间,2,因为每次你想访问其中一个值时,Python都必须将其拉出并"装箱"为int对象。

使用NumPy,您可以获得速度和空间,因为NumPy将允许您在严格优化的C循环中对整个阵列执行矢量化操作。


1.使用class在Python中创建的非内置类型呢?它们有一个指向dict的指针,您可以从Python中看到它是__dict__,它包含您添加的所有属性。因此,根据getsizeof,它们是24字节,但当然你也必须添加该dict的大小。

2.除非你不是。防止你的系统进入交换地狱可能会比装箱和开箱慢得多。而且,即使你没有避开那个巨大的悬崖,你可能仍然会避开涉及VM分页或缓存位置的较小悬崖

最新更新