写入较大numpy数组时Python解释器被终止的原因及解决方法
问题描述
在Python中使用numpy分配一个8GB级的数组并执行写入操作时,无论机器内存大小(哪怕是配备384GB内存的服务器),Python解释器都会被终止。相关代码如下:
import numpy as np A = np.empty(shape=(115, 4782969), dtype=np.complex128) # 替换为np.zeros结果一致 for i in range(115): for j in range(4782969): A[i,j] = 3+1.2j # 循环执行到i=80时触发终止 A.fill(3+1.2j) # 使用fill方法会更快触发终止
执行时的终端输出示例:
Python 3.12.5 | packaged by conda-forge | (main, Aug 8 2024, 18:36:51) [GCC 12.4.0] Type 'copyright', 'credits' or 'license' for more information IPython 8.26.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: import numpy as np In [2]: A = np.empty(shape=(115, 4782969), dtype=np.complex128) In [3]: A.fill(1.2) Killed
核心问题:
1)为何会出现这种情况?
2)如何避免该问题?
问题解答
1. 终止原因
这是系统OOM Killer(内存不足终止器)触发导致的,并非物理内存绝对不足,而是内存分配机制和限制共同作用的结果:
- 先计算数组实际大小:
115 * 4782969 * 16字节(complex128类型占16字节)≈ 8.2GB,但系统为进程分配内存时,还要预留Python解释器、numpy运行开销、系统基础内存等空间。 np.empty仅分配虚拟内存空间,未实际分配物理内存(写时复制机制)。当首次写入数据时,系统才会批量分配物理内存页,如果此时可用物理内存+交换空间不足以支撑,或者进程存在内存限制(如ulimit配置、cgroup配额),OOM Killer会直接终止Python进程——因为它是当前内存占用最高的进程之一。- 即使是大内存服务器,也可能存在内存限制配置,或有其他高内存进程在运行,导致实际可用内存无法一次性完成8GB级的物理内存分配。
2. 解决方法
方法1:分块处理数组
避免一次性写入整个数组,分小块分批处理,降低单次内存分配压力:
import numpy as np A = np.empty(shape=(115, 4782969), dtype=np.complex128) # 每次处理10行,可根据实际情况调整块大小 block_size = 10 for i in range(0, 115, block_size): end_idx = min(i + block_size, 115) A[i:end_idx] = 3 + 1.2j
方法2:使用内存映射(mmap)
将数组存储在磁盘上,通过内存映射访问,避免占用过多物理内存:
import numpy as np # 创建内存映射文件,存储到磁盘 A = np.memmap('large_array.npy', dtype=np.complex128, mode='w+', shape=(115, 4782969)) A[:] = 3 + 1.2j # 写入后刷新到磁盘 A.flush()
方法3:调整系统内存限制
- 查看当前进程内存限制:执行
ulimit -v,若限制值小于数组所需内存,可临时解除限制(仅当前终端有效):ulimit -v unlimited。 - 若服务器使用cgroup限制内存,需联系管理员调整对应进程组的内存配额。
方法4:直接初始化填充数组
跳过空数组创建步骤,直接生成已填充好的数组,numpy会更高效地分配内存:
A = np.full(shape=(115, 4782969), fill_value=3+1.2j, dtype=np.complex128)
内容的提问来源于stack exchange,提问作者Charles Bouillaguet
相关产品推荐
相关产品推荐

