奇怪的Python内存分配
在试图弄清楚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 =
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)