Python脚本疑似内存泄漏求助:处理FITS文件时循环崩溃
Python脚本内存泄漏引发OOM崩溃问题排查与解决
问题背景
运行Python脚本循环处理.fits文件:绘图并手动选点,将结果保存到.dat文件。该脚本在其他设备运行正常,但在新笔记本上仅完成3-4次外层循环后就崩溃,终端被直接终止或自行关闭。通过内存监控和系统日志确认,是内存耗尽被OOM Killer杀死。
脚本代码
import numpy as np from matplotlib import pyplot as plt from astropy.io import fits,ascii from glob import glob import gc list_files=np.sort(glob('*.fits')) ### around 90 objects list_ranges=ascii.read('ranges.dat')#### aroung 1000 objects for i in range(len(list_files)): output = open(list_files[i]+'.dat','w') with fits.open(list_files[i]) as single_file: x=single_file[0].data[0] y=single_file[0].data[1] for j in range(len(list_ranges)): x_single=x[((x<list_ranges[j]+3) & (x>list_ranges[j]-3))] y_single=y[((x<list_ranges[j]+3) & (x>list_ranges[j]-3))] fig, ax = plt.subplots(figsize=(18,8)) ax.plot(x,y) pts = np.asarray(plt.ginput(2, timeout=-1)) output.write('%.2f %.2f\n'%(pts[0,0],pts[1,0])) plt.close() del x_single,y_single,pts gc.collect() output.close() del single_file,x,y gc.collect()
内存监控信息
- 初始内存状态:
total used free shared buff/cache available Mem: 15Gi 2.6Gi 8.5Gi 754Mi 4.2Gi 11Gi Swap: 2.0Gi 0B 2.0Gi
- 处理第三个文件后的内存状态:
total used free shared buff/cache available Mem: 15Gi 5.5Gi 175Mi 7.8Gi 9.7Gi 1.7Gi Swap: 2.0Gi 3.0Mi 2.0Gi
OOM Killer日志(dmesg输出)
[ 5783.416916] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/user.slice/user-1000.slice/user@1000.service/app.slice/app-org.gnome.Terminal.slice/vte-spawn-94017a15-e67f-4443-87c5-a39220aa3d9c.scope,task=python3,pid=9500,uid=1000 [ 5783.416977] Out of memory: Killed process 9500 (python3) total-vm:9479428kB, anon-rss:4419828kB, file-rss:0kB, shmem-rss:2580kB, UID:1000 pgtables:14068kB oom_score_adj:0
解决方案
- 修复Matplotlib绘图资源泄漏:将
plt.close()改为plt.close(fig),明确关闭每个创建的Figure对象,避免未释放的绘图资源累积。每次绘图后可先清除Axes内容再关闭:ax.clear() plt.close(fig) - 优化文件IO操作:使用
with上下文管理器处理输出文件,替代手动open/close,确保文件资源正确释放:with open(list_files[i]+'.dat','w') as output: # 内层循环代码 - 减少重复计算:将
list_ranges转换为numpy数组,提升掩码计算效率;同时避免重复计算相同的掩码条件,可将掩码提取为临时变量:# 先转换为numpy数组(假设ranges.dat的目标列名为'value',根据实际调整) range_vals = list_ranges['value'].value # 内层循环中 mask = (x < range_vals[j] + 3) & (x > range_vals[j] - 3) x_single = x[mask] y_single = y[mask] - 避免过度依赖GC:移除不必要的
gc.collect()调用,Python垃圾回收机制会自动处理不再引用的对象。优先确保资源被显式释放(如关闭Figure、文件),而非依赖强制GC。 - 检查Matplotlib后端:若使用GNOME终端等桌面环境,确保Matplotlib使用的交互式后端(如TkAgg、Qt5Agg)能正确释放资源。可尝试在脚本开头添加:
plt.switch_backend('Qt5Agg') # 或其他适合当前环境的后端
内容的提问来源于stack exchange,提问作者Erasmo Trentin
相关产品推荐
相关产品推荐

