在填充列表时Python内存泄漏 - 如何解决?

1 投票
3 回答
955 浏览
提问于 2025-04-16 21:24

我有一段代码,看起来是这样的:

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

这四个文件每个大约150MB,每个文件里有大约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到1950MB之间,然后开始读取最后一个文件时,内存又增加了50到100MB,接着我就出现了错误。我尝试把最后一行(追加)注释掉,这样总内存使用量大约在500MB左右。

那么,我到底哪里做错了呢?这四个文件加起来大约600MB,但脚本在处理前三个文件时却消耗了大约1500MB,我实在搞不懂为什么……我该如何改善这个问题呢?谢谢。

3 个回答

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

你可以使用sqlite3这个内置模块来处理数据。你还可以用特殊的名字“:memory:”来代替“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()

撰写回答