在填充列表时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])
这四个文件每个大约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()