Утечка памяти Python при заполнении списка - как это исправить? - PullRequest
1 голос
/ 13 июля 2011

У меня есть фрагмент кода, который выглядит следующим образом:

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])

каждый из этих четырех файлов занимает около 150 МБ, а каждый содержит около 60 000 - 80 000 строк.

Я начал создавать сценарий, используя только один из этих файлов, потому что мне было проще тестировать функциональность таким образом, но теперь, когда у меня есть вся логика и функциональность, я, конечно, попытался запустить его на всех четырех файлах журнала одновременно.Когда скрипт начинает извлекать данные из четвертого файла, я получаю следующее:

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

Итак, я посмотрел, сколько памяти потребляет эта вещь, и вот что я нашел:

Скрипт читает первые три файла и достигает где-то около 1800-1950 МБ, затем начинает читать, последний файл увеличивается на 50-100 МБ больше, и затем я получаю ошибку.Я попытался запустить сценарий с последней строкой (добавлением), закомментированной, а затем он занимает около 500 МБ.

Итак, что я делаю не так?Эти четыре файла в совокупности занимают около 600 МБ, а сценарий потребляет около 1500 для заполнения списка только тремя из четырех файлов, что

Я не совсем понимаю, почему .. Как я могу улучшить это?Спасибо.

Ответы [ 3 ]

6 голосов
/ 13 июля 2011

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))
2 голосов
/ 13 июля 2011

Итерация непосредственно по содержимому файла:

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
1 голос
/ 13 июля 2011

Вы можете использовать встроенный модуль sqlite3 для манипулирования данными. Вы также можете указать специальное имя ": memory:" insted "c: / temp / example" для создания базы данных в оперативной памяти. Если не хранится в ОЗУ лимит свободного места на жестком диске.

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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...