代码之家  ›  专栏  ›  技术社区  ›  pstatix

array.array()如何使用这么小的内存空间?

  •  0
  • pstatix  · 技术社区  · 6 年前

    array.array() 类使用的内存比 sys.getsizeof

    from array import array
    a = array('f')
    for i in range(500000):
        a.append(float(i))
    sys.getsizeof(a)
    # 2100228
    sum(sys.getsizeof(i) for i in a)
    # 12000000 (makes sense, 24 bytes * 500K)
    # 2100228 + 12000000 = 14100228
    # 14100228 / 1000 = 14,100.228KB
    # 14,100.228 / 1000 = 14.1MB
    

    但是,在任务管理器中检查进程时,程序内存仅增加3MB。那么,这个过程如何只使用3MB,而对象却占用了14.1MB呢?

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

    蟒蛇 float 是一个功能齐全的对象,它知道它的类型(所以它有方法),并且可以被垃圾收集等等。在CPython(您可能正在使用的Python实现)中,它通过存储指向type对象的指针(8个字节)和一个引用计数(8个字节)以及实际的IEEE float64值(8个字节)来工作,因此它至少有24个字节长。

    一个 list 只存储对Python对象的引用。所以,a 列表 在50万个float中,列表本身(存储所有这些引用)的大小将超过4MB,加上所有引用的大小 浮动 总共还需要12兆字节。

    一个 array.array 浮动 对象,它只存储IEEE float64值(8字节)的位,然后创建 浮动 arr[0] . 这使它小得多整个事情只需要4MB的内存,但也较慢。 1个

    当然,您甚至没有存储IEEE float64的数组(即 d f ),但浮动32。其中50万需要200亿。

    如果你想两者兼得,第三方库NumPy可以以同样的方式存储这些位 是的,它可以对这些位进行计算,而不必创建和销毁 到处都是东西,所以都比较小


    所以,当你要求一个500K的数组大小时 f型

    但是当你在数组上循环,计算每个成员的大小时,你实际上创建了24字节 浮动 飞行中的物体。所有这些临时对象的总大小为12MB。但是他们是暂时的,一旦你检查了每一个字节的大小,你就忘记了它,它就变成了垃圾并被清理干净,同样的24个字节可以被重新用于下一个字节。


    至于为什么任务管理器显示内存增加3MB:

    所以,假设解释器的堆中还有2 MB的空间,您要求它分配一个4 MB的对象。它需要回到Windows,并要求至少多出2MB的内存。它得到的比它需要的多一点(所以它不需要马上回去要求更多),结果是大约3MB。当然,这只是最终从操作系统中获得3MB的许多方法之一,而要弄清楚究竟发生了什么需要复杂的调试(比做更多有用的事情更复杂,比如只跟踪程序的实际堆使用)。

    如您所见,这使得任务管理器测量内存使用量非常无用,除了非常宽泛的笔划。(事实上,更糟糕的是,一旦您遇到诸如Python何时向Windows返回空闲内存、内存碎片化时会发生什么情况、OS是否超量使用、页面何时可以或不能在虚拟内存中重新映射以及其他各种复杂性的问题。)


    一。尽管它并不总是慢。有时候,内存更紧凑在缓存或虚拟内存方面给了你如此多的优势,以至于它无法弥补在创建和销毁所有对象时浪费的时间。

        2
  •  3
  •   user2357112    6 年前

    你的 a 数组实际上不包含由 for i in a . 这些对象是在访问时生成的。 包含原始32位浮点,而不是浮点对象。

        3
  •  0
  •   tif    6 年前

    docs ,“的 array 模块定义一个对象类型,该对象类型可以 紧凑的 a[i] 将需要存储类型信息,而对于整个 a 数组只需存储一次。