2011-07-13 5 views
1

私のようなコードがありますリストを作成中にPythonのメモリリークが発生する - その修正方法は?

downloadsByExtensionCount = defaultdict(int) 
downloadsByExtensionList = [] 
logFiles = ['file1.log', 'file2.log', 'file3.log', 'file4.log'] 


for logFile in logFiles: 
    log = open(logFile, 'r', encoding='utf-8') 
    logLines = log.readlines() 

    for logLine in logLines: 
     date, time, c_ip, cs_method, s_port, cs_uri_stem, cs_user_agent = logLine.split(" ") 

     downloadsByExtensionCount[cs_uri_stem] += 1 
     downloadsByExtensionList.append([date, time, c_ip, cs_method, s_port, cs_uri_stem, cs_user_agent]) 

をこれら4つのファイルのそれぞれは、150メガバイトの周りで、それぞれが約60 000持っている - それで80 000行を。

私はこのような機能をテストする方が速いので、これらのファイルのうちの1つだけを使用してスクリプトを作成しましたが、今ではすべてのロジックと機能があります。一度。私はこの事を消費しているどのくらいのメモリを見ていたが、これは私が見つけたものである - そう

Traceback (most recent call last): 
    File "C:\Python32\lib\codecs.py", line 300, in decode 
    (result, consumed) = self._buffer_decode(data, self.errors, final) 
MemoryError 

スクリプトどのようなスクリプトは、第四のファイルからデータをフェッチを開始したときに私が取得することはこれです最初の3つのファイルを読み込み、1800-1950MB程度に達したら、最後のファイルの読み込みが50-100MB増えてエラーが発生します。 私は最後の行(append)をコメントアウトしてスクリプトを実行しようとしたが、合計で約500MBに達した。

私は間違っていますか?これらの4つのファイルの合計は約600MBであり、スクリプトは4つのファイルのうち3つで3つのリストを作成するために約1500を消費します。

どうして私はこれを改善できますか?ありがとうございました。直接ファイルの内容によって

答えて

1

データ操作にsqlite3組み込みモジュールを使用できます。特別な名前 ":memory:" insted "c:/ temp/example"を指定して、RAMにデータベースを作成することもできます。 RAMに格納されていない場合、ハードディスクの空き容量が制限されます。

import sqlite3 
from collections import defaultdict 

downloadsByExtensionCount = defaultdict(int) 
# downloadsByExtensionList = [] 
logFiles = ['file1.log', 'file2.log', 'file3.log', 'file4.log'] 


conn = sqlite3.connect('c:/temp/example') 
c = conn.cursor() 
# Create table 
c.execute('create table if not exists logs(date, time, c_ip, cs_method, s_port, cs_uri_stem, cs_user_agent)') 

for logFile in logFiles: 
    try: 
     log = open(logFile, 'rb')#, encoding='utf-8') 
    except IOError, e: 
     continue 

    logLines = log.readlines() 

    for logLine in logLines: 
     date, time, c_ip, cs_method, s_port, cs_uri_stem, cs_user_agent = logLine.split(" ") 

     downloadsByExtensionCount[cs_uri_stem] += 1 
     c.execute(
      'insert into logs(date, time, c_ip, cs_method, s_port, cs_uri_stem, cs_user_agent) values(?,?,?,?,?,?,?)', 
      (date, time, c_ip, cs_method, s_port, cs_uri_stem, cs_user_agent) 
      ) 

conn.commit() 
conn.close() 
2

反復:

for logFile in logFiles: 

    log = open(logFile, 'r', encoding='utf-8') 
    for logLine in log: 
     ... 
    log.close() 

利用tuple代わりのlist

>>> sys.getsizeof(('1','2','3')) 
80 
>>> sys.getsizeof(['1','2','3']) 
96 
6

log.readlines()は、行のリストにファイルの内容を読み取ります。そのファイルを直接反復して、余分なリストを避けることができます。

downloadsByExtensionCount = defaultdict(int) 
downloadsByExtensionList = [] 
logFiles = ['file1.log', 'file2.log', 'file3.log', 'file4.log'] 


for logFile in logFiles: 
    # closes the file after the block 
    with open(logFile, 'r', encoding='utf-8') as log: 
     # just iterate over the file 
     for logLine in log: 
      date, time, c_ip, cs_method, s_port, cs_uri_stem, cs_user_agent = logLine.split(" ") 
      downloadsByExtensionCount[cs_uri_stem] += 1 
      # tuples are enough to store the data 
      downloadsByExtensionList.append((date, time, c_ip, cs_method, s_port, cs_uri_stem, cs_user_agent)) 
+1

Jochenは正しいです - ファイルを反復処理する正しい方法です。私が追加したいことの1つは、多くのファイルを繰り返し処理する必要がある場合、 'with open(filename)as f:'メソッドを使用する必要があります。これは、ファイルの処理を完了したときにガベージコレクタそれを閉じます。 – synthesizerpatel

+0

ファイルを反復処理する "with"メソッドを使用する場合、fileName.close()が必要ですか?私はちょっとグーグルグーグルで、それは答えがノーだと思われる? – pootzko

+0

タプルを使用し、メソッドスクリプトを使用すると、はるかに高速に実行され、200〜300MBも節約できますが、私はまだMemoryErrorを取得します。 – pootzko

関連する問題