2012-08-03 110 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

相關問題