奇怪的Python内存分配

奇怪的Python内存分配,python,numpy,memory-profiling,Python,Numpy,Memory Profiling,在试图弄清楚Python的垃圾收集系统是如何工作的时候,我偶然发现了这个奇怪的现象。运行以下简单代码: import numpy as np from memory_profiler import profile @profile def my_func(): a = np.random.rand(1000000) a = np.append(a, [1]) a = np.append(a, [2]) a = np.append(a, [3]) a =

在试图弄清楚Python的垃圾收集系统是如何工作的时候,我偶然发现了这个奇怪的现象。运行以下简单代码:

import numpy as np
from memory_profiler import profile

@profile
def my_func():
    a = np.random.rand(1000000)
    a = np.append(a, [1])
    a = np.append(a, [2])
    a = np.append(a, [3])
    a = np.append(a, [4])
    a = np.append(a, [5])
    b = np.append(a, [6])
    c = np.append(a, [7])
    d = np.append(a, a)

    return a

if __name__ == '__main__':
    my_func()
在MacBook上使用memory_profiler版本0.52和Python 3.7.6,我得到了以下输出:

Line #    Mem usage    Increment   Line Contents
================================================
     4     54.2 MiB     54.2 MiB   @profile
     5                             def my_func():
     6     61.8 MiB      7.7 MiB       a = np.random.rand(1000000)
     7     69.4 MiB      7.6 MiB       a = np.append(a, [1])
     8     69.4 MiB      0.0 MiB       a = np.append(a, [2])
     9     69.4 MiB      0.0 MiB       a = np.append(a, [3])
    10     69.4 MiB      0.0 MiB       a = np.append(a, [4])
    11     69.4 MiB      0.0 MiB       a = np.append(a, [5])
    12     69.4 MiB      0.0 MiB       b = np.append(a, [6])
    13     77.1 MiB      7.6 MiB       c = np.append(a, [7])
    14     92.3 MiB     15.3 MiB       d = np.append(a, a)
    15                             
    16     92.3 MiB      0.0 MiB       return a
有两件事很奇怪。首先,为什么第7行的内存增长比第8-11行明显?第二,为什么第12行的内存增长不如第13行


请注意,如果删除第12-14行,第7行中的内存仍然会增加。因此,第12行中的内存实际上增加了,但内存分析器在第7行中错误地显示了内存的增加,这不是一个bug。

在第7行中,这可能是分析器的某种形式的开销吗?通过使用
sys.getsizeof()
可以看到,数组在每次追加时都会增加8个字节,而不会突然跳转

起初,我认为这可能与Python列表的情况类似,在Python列表中,内存每4个附加项在32字节的块中分配一次,但情况似乎并非如此

如果没有函数或分析器,我看不到与您在帖子中显示的行为类似的行为。我能看到的唯一奇怪之处是
d
的大小并不是
a
的两倍

将numpy导入为np
导入系统
a=np.rand.rand(1000000)
sys.getsizeof(a)
Out[55]:800096
a=np.append(a[1])
sys.getsizeof(a)
Out[57]:8000104
a=np.append(a[2])
sys.getsizeof(a)
Out[59]:8000112
a=np.append(a[3])
sys.getsizeof(a)
Out[61]:8000120
a=np.append(a[4])
sys.getsizeof(a)
Out[63]:8000128
a=np.append(a[5])
sys.getsizeof(a)
Out[66]:8000136
a=np.append(a[6])
sys.getsizeof(a)
Out[68]:8000144
a=np.append(a[7])
sys.getsizeof(a)
Out[71]:8000152
d=np.追加(a,a)
sys.getsizeof(d)
Out[73]:16000208

创建
a
生成一个8e6字节的数组(选中'a.nybtes)

np.append
创建了一个新数组(它是
串联
,而不是列表append),因此我们又增加了8MB

 7     69.4 MiB      7.6 MiB       a = np.append(a, [1])
我的猜测是,在下面的步骤中,它使用这两个8MB块来回循环
numpy
不会返回(操作系统)每个空闲块

然后将新数组分配给
c
<代码>a与
b
一起仍然存在。(我第一次看这个时错过了
b

d
的大小是
a
的两倍,因此这是15MB跳转的原因<代码>a、b、c仍然存在

14     92.3 MiB     15.3 MiB       d = np.append(a, a)
b
c
只是比
a
大一到两个数字,所以每个数字大约占8MB。这似乎说明了一切

在跟踪内存使用情况时,请记住
numpy
python
OS
都起到了作用。我们大多数人不知道所有的细节,所以我们只能粗略地猜测到底发生了什么

13     77.1 MiB      7.6 MiB       c = np.append(a, [7])
14     92.3 MiB     15.3 MiB       d = np.append(a, a)