为何存储NumPy数组会导致迭代时内存占用持续攀升?
我遍历asm文件列表,提取数组后关闭文件。此前遇到的内存占用问题现已解决:最初将提取的NumPy数组存入列表时,每次迭代内存占用不断累积;改为先调用tolist()转成Python列表再存入后,问题得以解决。现咨询为何存储NumPy数组会引发该内存问题。
代码如下:
t1_start = process_time() files = os.listdir('asmFiles') i_arrays=[] file_names=[] for i in tqdm(files[0:1501]): f_name=i.split('.')[0] file_names.append(f_name) b='asmFiles/'+str(i) f=open(b,'rb') ln=os.path.getsize(b) width=int(ln**0.5) rem=ln%width a = array.array("B") a.fromfile(f,ln-rem) f.close() g=np.reshape(a,(int(len(a)/width),width)) g=np.uint(g) h=g[0][0:800] i_arrays.append(h.tolist()) print(psutil.virtual_memory()[2]) t1_stop = process_time() print("Elapsed time during the whole program in seconds:",t1_stop-t1_start)
原因分析
NumPy数组的视图特性是核心问题
NumPy的切片操作默认返回的是原数组的视图,而非数据副本。也就是说你代码里的h = g[0][0:800]并没有复制g的前800个元素,只是创建了一个指向g底层内存块的"窗口"。当你把h存入列表i_arrays时,相当于保留了对整个g数组的间接引用——哪怕循环结束后局部变量g被销毁,只要h还在列表里,g的完整内存块就不会被垃圾回收。NumPy数组的内存结构导致无法及时释放
NumPy数组由两部分组成:存储元数据(形状、数据类型等)的Python对象,以及存储实际数据的C风格连续内存块。列表中存储的是NumPy数组对象的引用,只要这个引用存在,对应的C内存块就会被锁定,无法被系统回收。而你处理的每个asm文件转换后的g数组体积不小,1500次迭代下来,累积的未释放内存自然会导致占用飙升。Python列表切断了内存引用链
调用h.tolist()时,会把NumPy数组的元素逐个转换成Python原生整数,并生成一个独立的Python列表。这个操作会创建数据的完整副本,彻底切断了和原数组g的视图关联。循环结束后,g没有任何引用指向它,就能被Python垃圾回收器及时清理,释放掉大内存块。虽然单个Python整数的内存开销比NumPy的uint8大,但你只保留了800个元素,整体内存占用完全可控。
额外优化建议
- 如果想继续使用NumPy数组而不转成Python列表,可以在切片时显式创建副本:
h = g[0][0:800].copy(),这样就能切断和g的视图关联,让g的内存可以被及时回收。 - 处理超大asm文件时,可考虑分块读取转换,避免一次性加载整个文件到内存。
内容的提问来源于stack exchange,提问作者Zerorad

