Numpy数组拼接(concatenate/vstack)后内存占用居高不下问题排查
问题分析与解决方案
首先,你遇到的内存占用未回落问题,大概率不是Python垃圾回收的问题,而是和Numpy内存管理机制以及操作系统内存回收逻辑有关,咱们一步步拆解:
1. 为什么手动GC后内存没下降?
操作系统不会立刻回收闲置内存
当Python(或Numpy)释放内存时,操作系统通常不会马上把这些内存从进程的RSS(Resident Set Size,即psutil测量的内存)中移除。相反,它会把这块内存标记为「可用」,留给你的进程后续复用——只有当系统内存紧张时,操作系统才会将闲置内存回收给其他进程。
所以你用psutil看到的内存占用没降,不代表原数据集的内存没被释放,只是操作系统还没把这块内存收回去而已。
Numpy的内存池机制
Numpy自身维护了一个内存池,用来高效分配和管理数组内存。当你删除Numpy数组时,内存会被归还给Numpy的内存池,而非直接还给操作系统。这部分内存会被Numpy用来创建新数组,同样不会立刻体现在进程RSS的下降上。
2. 如何验证内存是否真的被释放?
你可以做个简单测试来确认:
data_set = _get_data('someName') gc.collect() # 创建一个和原training_data尺寸相近的数组 test_arr = np.random.rand(*training_data.x.shape) # 再次测量内存 memoryUse = py.memory_info()[0] / 2. ** 30 print('memory after creating new array:', memoryUse)
如果创建新数组后内存没有明显增长,说明原数据集的内存已经被回收并复用了,只是操作系统未归还而已。
3. 排查隐藏引用问题
如果上述测试显示内存仍持续增长,那就要检查是否有隐藏引用未被释放:
- 检查
from_file方法:这个方法里有没有缓存机制?比如把加载的数组存在全局变量或类属性中?如果有,原数据集的数组会被这些缓存引用,导致无法被GC回收。 - 检查其他引用源:有没有在日志、全局变量、回调函数等其他地方保留了
training_data或test_data的引用?
4. 强制释放内存的小技巧
如果确实需要让内存归还给操作系统,可以尝试手动释放Numpy数组的内存:
def _get_data(self, data_set_name): training_data = DataSet.from_file('path_to_data_file','path_to_label_file') test_data = DataSet.from_file('path_to_data_file','path_to_label_file') merged_data = training_data.concat(test_data) # 手动删除原数据集的数组引用,强制释放内存 del training_data.x del training_data.y del training_data del test_data.x del test_data.y del test_data gc.collect() return merged_data
注意:这种方法不一定能让RSS立刻下降,但能确保原数组的内存被标记为可回收。
另外,你也可以尝试从根源减少内存占用——比如直接从文件加载数据到合并后的数组中,避免创建中间数据集对象。
内容的提问来源于stack exchange,提问作者Yannic Bürgmann
相关产品推荐
相关产品推荐

