代码之家  ›  专栏  ›  技术社区  ›  Frederico Schardong

为什么我的Python2.7进程使用的内存量越来越大?

  •  1
  • Frederico Schardong  · 技术社区  · 6 年前

    考虑到列表只有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 )使内存增加10倍。

    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的内存需求,从而有效地创建一个列表来使用几乎所有的可用内存,同时防止操作系统进行交换吗?

    1 回复  |  直到 6 年前
        1
  •  6
  •   abarnert    6 年前

    这个 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)
    

    然后结果会更有意义。


    但请注意,如果您没有创建 range 如果有10个唯一的整数,但是,比方说,从0到10000的10个随机整数,或者10个0的副本,这个计算将不再正确。所以如果你想处理这些案子,你需要做一些事情比如 id 到目前为止你所看到的每一个对象,并跳过对同一对象的任何其他引用 身份证件 .

    Python 2.x文档过去有一个指向旧递归的链接 getsizeof公司 这样做的函数,更多的是链接死了,所以它被删除了。

    The 3.x docs 链接到 a newer one ,在Python2.7中可能工作,也可能不工作。(我一眼就注意到它使用了 __future__ 声明 print ,然后从 reprlib.repr repr ,所以可能是这样。)


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

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


    如果您想知道如何避免每个整数使用24字节,那么答案是 NumPy's ndarray 或者,如果因为某种原因你不能,stdlib array.array .

    其中一个允许您指定“本机类型”,如 np.int32 对于纽比或 i 对于 数组.array ,并创建一个数组,该数组直接保存这些原生类型值中的100M。每个值只需要4个字节,加上几十个固定字节的头开销,这比 list 的8个字节的指针,加上结尾处随长度而变化的一点松弛,再加上 内景 对象包装每个值。

    使用 数组.array ,你牺牲了速度来换取空间, 2个 因为每次您想要访问其中一个值时,Python都必须将其拉出并将其作为 内景 反对。

    使用NumPy,你得到了 二者都 速度和空间,因为NumPy将允许您在一个紧密优化的C循环中对整个数组执行矢量化操作。


    一。在Python中使用 class ? 它们有一个指向dictal的指针,从Python land可以看到 __dict__ 包含您添加的所有属性。所以它们是24字节 getsizeof公司 ,但当然还必须添加dict的大小。

    2。除非你不这样做。阻止你的系统进入交换地狱可能会加速比拳击和解体更慢的事情。即使你没有避开那巨大的悬崖 仍然 避免涉及VM分页或缓存位置的小悬崖。