为什么我的python进程会占用这么多内存?

为什么我的python进程会占用这么多内存?,python,optimization,memory,numpy,Python,Optimization,Memory,Numpy,我正在从事一个项目,该项目涉及使用python来读取、处理和写入文件,这些文件有时多达几百兆字节。当我试图处理一些特别大的文件时,程序偶尔会失败。它没有说“内存错误”,但我怀疑这就是问题所在(事实上,它根本没有给出失败的理由) 我一直在较小的文件上测试代码,并观看“top”以了解内存使用情况,它通常达到60%。上面说我有4050352k的总内存,所以是3.8Gb 同时,我正试图通过以下代码跟踪python本身的内存使用情况(请参阅我的问题): mem = 0 for variable in di

我正在从事一个项目,该项目涉及使用python来读取、处理和写入文件,这些文件有时多达几百兆字节。当我试图处理一些特别大的文件时,程序偶尔会失败。它没有说“内存错误”,但我怀疑这就是问题所在(事实上,它根本没有给出失败的理由)

我一直在较小的文件上测试代码,并观看“top”以了解内存使用情况,它通常达到60%。上面说我有4050352k的总内存,所以是3.8Gb

同时,我正试图通过以下代码跟踪python本身的内存使用情况(请参阅我的问题):

mem = 0
for variable in dir():
    variable_ = vars()[variable]
    try: 
        if str(type(variable_))[7:12] == 'numpy':
            numpy_ = True
        else:
            numpy_ = False
    except:
        numpy_ = False
    if numpy_:
        mem_ = variable_.nbytes
    else:
        mem_ = sys.getsizeof(variable)
    mem += mem_
    print variable+ type: '+str(type(variable_))+' size: '+str(mem_)
print 'Total: '+str(mem)
在运行该块之前,我将所有不需要的变量设置为无,关闭所有文件和图形等。在该块之后,我使用subprocess.call()运行下一阶段处理所需的fortran程序。在fortran程序运行时查看顶部显示fortran程序使用了~100%的cpu和~5%的内存,python使用了0%的cpu和53%的内存。然而,我的一小段代码告诉我,python中的所有变量加起来只有23Mb,应该是~0.5%

发生了什么事?我不希望那个小片段能让我了解内存使用情况,但它应该精确到几Mb以内?或者仅仅是top没有注意到内存已经被释放,但是如果需要,它可以被其他需要它的程序使用

根据要求,下面是代码的一个简化部分,它将耗尽所有内存(file_name.cub是一个ISIS3多维数据集,它是一个包含5层(带)的文件)对于同一张地图,第一层是光谱辐射,接下来的4层与纬度、经度和其他细节有关。我正在尝试处理一张来自火星的图像。StartByte是我以前从.cub文件的ascii头读取的值,告诉我数据的起始字节,样本和线条是地图的尺寸,也读取从页眉开始。):

latitude\u数组='cheese'#一会儿就有意义了
f_to=open('to_file.dat','w'))
f_rad=open('file_name.cub','rb')
f_rad.seek(0)
header=struct.unpack(“%dc%”(StartByte-1),f_rad.read(StartByte-1))
页眉=无
#
f_lat=open('file_name.cub','rb')
横向搜索(0)
header=struct.unpack(“%dc%”(StartByte-1),f_lat.read(StartByte-1))
页眉=无
pre=struct.unpack(“%df%”(示例*行),f_lat.read(示例*行*4))
pre=无
#
f_lon=open('file_name.cub','rb')
f_lon.seek(0)
header=struct.unpack(“%dc%”(StartByte-1),f_lon.read(StartByte-1))
页眉=无
pre=struct.unpack(“%df%”(样本*行*2),f_lon.read(样本*行*2*4))
pre=无
#(其他两个乐队的情况也差不多)
#所以header和pre只是为了到达文件的正确部分,并且
#然后设置为“无”。我确实尝试过使用seek(),但在某些情况下不起作用
#原因,我最终用了这个技巧。
对于范围内的行(行):
sample_rad=struct.unpack(“%df%”(示例),f_rad.read(示例*4))
sample\u rad=np.数组(sample\u rad)

sample_rad[sample_rad仅仅因为您已经延迟了变量,并不意味着Python进程已经将分配的内存返回给了系统。请参阅


如果
gc.collect()
不适用于您,请调查使用IPC在子进程中分叉和读取/写入文件的情况。这些进程将在完成后结束,并将内存释放回系统。您的主进程将继续以低内存使用率运行。

我在长时间运行的进程中使用numpy时遇到过类似的问题-它可能会泄漏就像一个筛子或碎片堆一样。你是在创建大量numpy数组并销毁它们吗?Python在销毁某些对象实例后不会立即将内存释放回系统。它有一些对象池,称为Arena,需要一段时间才能释放。在某些情况下,你可能会受到内存不足的困扰这也会导致进程的内存使用量增加。
sys.getsizeof()
不是测试内存使用情况的可靠方法。首先,它只跟踪给定Python对象的内存,而不跟踪它对内存中其他项的引用。其次,根据文档,它不能保证对第三方扩展正常工作。还有,@C2H5OH所说的。@YannRamin:是的,我从多个文件中分别读取一行一次,将它们转换为numpy数组,然后我对数组执行一些运算,然后将结果发送到另一个文件。然后使用原始文件的下一行再次执行。查看正在运行的消耗内存的实际代码会很有帮助。也许这可以改进。谢谢,该链接提供了信息。gc.collect如果没有效果,请您再解释一点关于分叉的内容好吗?将上面的代码(在“for line in line”循环中)放入“os.waitpid(0,0)/newpid=os.fork()/如果newpid==0:”这样做吗?如果你可以使用子流程模块而不是显式分叉,通常会更好。但基本思想是,如果你有4个任务,每个任务需要1GB的内存(即使它们只需要很短的内存),有四个单独的子进程,每个子进程使用1GB加上一点开销,而不是一个父进程使用4GB并崩溃,这是一件好事。(在64位操作系统上,这不一定是真的,特别是当你崩溃时,因为你的总系统RAM+交换空间或页面空间不足,而不是操作系统内存空间不足,但值得一试。)@EddyTheB:这可能是个正确的想法。子流程模块可能也很合适。但是有了这两个模块,我假设你明白在流程之间共享数据并不容易。而且听起来你需要这样做,因为你在读、处理和写……续。将流程分解为离散操作是最好的选择我可以给出st建议。可能有一个(或多个)子进程读取da
latitude_array = 'cheese'   # It'll make sense in a moment
f_to = open('To_file.dat','w') 

f_rad = open('file_name.cub', 'rb')
f_rad.seek(0)
header=struct.unpack('%dc' % (StartByte-1), f_rad.read(StartByte-1))
header = None    
#
f_lat = open('file_name.cub', 'rb')
f_lat.seek(0)
header=struct.unpack('%dc' % (StartByte-1), f_lat.read(StartByte-1))
header = None 
pre=struct.unpack('%df' % (Samples*Lines), f_lat.read(Samples*Lines*4))
pre = None
#
f_lon = open('file_name.cub', 'rb')
f_lon.seek(0)
header=struct.unpack('%dc' % (StartByte-1), f_lon.read(StartByte-1))
header = None 
pre=struct.unpack('%df' % (Samples*Lines*2), f_lon.read(Samples*Lines*2*4))
pre = None
# (And something similar for the other two bands)
# So header and pre are just to get to the right part of the file, and are 
# then set to None. I did try using seek(), but it didn't work for some
# reason, and I ended up with this technique.
for line in range(Lines):
    sample_rad = struct.unpack('%df' % (Samples), f_rad.read(Samples*4))
    sample_rad = np.array(sample_rad)
    sample_rad[sample_rad<-3.40282265e+38] = np.nan  
    # And Similar lines for all bands
    # Then some arithmetic operations on some of the arrays
    i = 0
    for value in sample_rad:
        nextline = sample_lat[i]+', '+sample_lon[i]+', '+value # And other stuff
        f_to.write(nextline)
        i += 1
    if radiance_array == 'cheese':  # I'd love to know a better way to do this!
        radiance_array = sample_rad.reshape(len(sample_rad),1)
    else:
        radiance_array = np.append(radiance_array, sample_rad.reshape(len(sample_rad),1), axis=1)
        # And again, similar operations on all arrays. I end up with 5 output arrays
        # with dimensions ~830*4000. For the large files they can reach ~830x20000
f_rad.close()
f_lat.close()
f_to.close()   # etc etc 
sample_lat = None  # etc etc
sample_rad = None  # etc etc

#
plt.figure()
plt.imshow(radiance_array)
# I plot all the arrays, for diagnostic reasons

plt.show()
plt.close()

radiance_array = None  # etc etc
# I set all arrays apart from one (which I need to identify the 
# locations of nan in future) to None

# LOCATION OF MEMORY USAGE MONITOR SNIPPET FROM ABOVE