2012-08-03 105 views
9

我正在研究一个涉及使用python读取,处理和写入有时大至几百兆字节的文件的项目。当我尝试处理一些特别大的文件时,程序偶尔会失败。它不会说'记忆错误',但我怀疑这是问题(实际上它没有理由失败')。为什么我的python进程会占用这么多内存?

我一直在测试较小文件上的代码并观察'顶部'来查看内存使用情况是什么样的,它通常会达到60%。顶部说我有4050352k的总内存,所以3.8Gb。

同时我试图跟踪与下面的代码有点蟒蛇本身的内存使用情况(请参阅从yesterday我的问题):

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) 

之前,我运行块我设置的所有变量我不不需要None,关闭所有的文件和图形等等。之后,我使用subprocess.call()来运行下一阶段处理所需的fortran程序。在Fortran程序运行时查看顶部显示fortran程序正在使用〜100%的cpu和〜5%的内存,并且python使用cpu的0%和53%的内存。然而,我的一小段代码告诉我,python中的所有变量加起来只有23Mb,应该是〜0.5%。

那么发生了什么?我不希望这个小片段能给我一个关于内存使用情况的地方,但它应该准确地在几Mb以内?还是仅仅是顶层没有注意到内存已经被放弃了,但是如果有必要的话,其他程序可以使用它?

按照要求,这是使用所有内存的代码的简化部分(file_name.cub是一个ISIS3多维数据集,它是一个包含相同映射的5层(带)的文件,第一层是光谱发光,接下来的4个与纬度,经度和其他细节有关,它是来自Mars想要处理的图像StartByte是我以前从.cub文件的ascii标题读取的值,告诉我开始的字节数据,样品和线条地图,还从报头读取的尺寸):

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 

所以我在有关打开多个文件的意见撒谎,这是同一个文件的多个实例。我只继续使用一个未设置为None的数组,它的大小为〜830x4000,尽管这在某种程度上构成了我可用内存的50%。我也试过gc.collect,但没有改变。我很乐意听到关于如何改进任何代码(关于这个问题或其他方面)的建议。

也许我应该提一下:原本我打开的文件是全部的(即不是像上面那样一行一行地打开),一行一行地完成是最初尝试节省内存。

+0

我在长时间运行的过程中使用numpy时遇到过类似的问题 - 它会像筛子一样泄漏或像堆栈一样碎裂。你是否创建了很多numpy数组并摧毁它们? – 2012-08-03 17:38:13

+3

Python在破坏*某个对象实例之后不会立即将内存释放回系统。它有一些对象池,称为竞技场,需要一段时间才会被释放。在某些情况下,您可能会遭受内存碎片,这也会导致进程的内存使用量增加。 – C2H5OH 2012-08-03 17:38:40

+1

'sys.getsizeof()'不是测试内存使用情况的可靠方法。首先,它只跟踪给定Python对象的内存,而不是它对内存中其他项的引用。其次,根据文档,不能保证为第三方扩展程序正常工作。另外,@ C2H5OH说。 – 2012-08-03 17:40:20

回答

9

仅仅因为你已经引用了你的变量并不意味着Python进程已经将分配的内存返回给了系统。见How can I explicitly free memory in Python?

更新

如果GC.Collect的()不为你工作,调查分叉和读/写在使用IPC子进程文件。这些进程在完成时会结束并将内存释放回系统。您的主进程将继续以低内存使用率运行。

+0

谢谢,该链接已提供信息。 gc.collect没有任何作用,请你能解释一下更多关于分叉的问题吗?将推杆上面的代码(内部的“在线”线循环)一个内部“os.waitpid(0,0)/ newpid = os.fork()/如果newpid == 0:”这样的伎俩? – EddyTheB 2012-08-03 19:24:27

+0

如果您可以使用子进程模块而不是明确分叉,那通常会更好。但基本的想法是,如果你有4个任务,每个任务需要1GB的内存(即使他们只需要它),有4个独立的子进程,每个进程使用1GB加上一点开销,而不是一个父进程4GB和崩溃是一件好事。 (这在64位操作系统上并不一定是这样,特别是当你因为系统内存+交换空间或页面空间而不是操作系统内存空间而崩溃时,尤其值得一提的是,这是值得的。) – abarnert 2012-08-03 19:34:20

+0

@EddyTheB:这可能是正确的想法。子流程模块也可能是合适的。但是对于其中的任何一种,我假设您明白在这些流程之间共享数据并不容易。这听起来像你需要,因为你正在阅读,处理和写作。 – bioneuralnet 2012-08-04 02:59:00

相关问题