Python 循环期间释放内存
我的代码中有内存错误。我的解析器可以这样概括:Python 循环期间释放内存,python,json,performance,optimization,out-of-memory,Python,Json,Performance,Optimization,Out Of Memory,我的代码中有内存错误。我的解析器可以这样概括: # coding=utf-8 #! /usr/bin/env python import sys import json from collections import defaultdict class MyParserIter(object): def _parse_line(self, line): for couple in line.split(","): key, value = c
# coding=utf-8
#! /usr/bin/env python
import sys
import json
from collections import defaultdict
class MyParserIter(object):
def _parse_line(self, line):
for couple in line.split(","):
key, value = couple.split(':')[0], couple.split(':')[1]
self.__hash[key].append(value)
def __init__(self, line):
# not the real parsing just a example to parse each
# line to a dict-like obj
self.__hash = defaultdict(list)
self._parse_line(line)
def __iter__(self):
return iter(self.__hash.values())
def to_dict(self):
return self.__hash
def __getitem__(self, item):
return self.__hash[item]
def free(self, item):
self.__hash[item] = None
def free_all(self):
for k in self.__hash:
self.free(k)
def to_json(self):
return json.dumps(self.to_dict())
def parse_file(file_path):
list_result = []
with open(file_path) as fin:
for line in fin:
parsed_line_obj = MyParserIter(line)
list_result.append(parsed_line_obj)
return list_result
def write_to_file(list_obj):
with open("out.out", "w") as fout:
for obj in list_obj:
json_out = obj.to_json()
fout.write(json_out + "\n")
obj.free_all()
obj = None
if __name__ == '__main__':
result_list = parse_file('test.in')
print(sys.getsizeof(result_list))
write_to_file(result_list)
print(sys.getsizeof(result_list))
# the same result for memory usage result_list
print(sys.getsizeof([None] * len(result_list)))
# the result is not the same :(
其目的是解析(大型)文件,每一行都转换为一个json对象,并将其写回一个文件
我的目标是减少占用空间,因为在某些情况下,此代码会引发内存错误。每次fout.write之后
我想删除(空闲内存)obj
reference
我试图将obj
设置为无调用方法obj.free_all()
,但没有一个方法释放内存。我还使用了simplejson而不是json,这减少了占用空间,但在某些情况下仍然太大
test.in看起来像:
test1:OK,test3:OK,...
test1:OK,test3:OK,...
test1:OK,test3:OK,test4:test_again...
....
不要在数组中存储很多类的实例,而是内联存储。例如
% cat test.in
test1:OK,test3:OK
test1:OK,test3:OK
test1:OK,test3:OK,test4:test_again
% cat test.py
import json
with open("test.in", "rb") as src:
with open("out.out", "wb") as dst:
for line in src:
pairs, obj = [x.split(":",1) for x in line.rstrip().split(",")], {}
for k,v in pairs:
if k not in obj: obj[k] = []
obj[k].append(v)
dst.write(json.dumps(obj)+"\n")
% cat out.out
{"test1": ["OK"], "test3": ["OK"]}
{"test1": ["OK"], "test3": ["OK"]}
{"test1": ["OK"], "test3": ["OK"], "test4": ["test_again"]}
如果速度慢,不要逐行写入文件,而是将转储的json字符串存储在数组中,并执行
dst.write(“\n”.join(array))
为了使obj
能够释放,必须消除对它的所有引用。您的循环没有这样做,因为list\u obj
中的引用仍然存在。以下内容将解决此问题:
def write_to_file(list_obj):
with open("out.out", "w") as fout:
for ix in range(list_obj):
obj = list_obj[ix]
list_obj[ix] = None
json_out = obj.to_json()
fout.write(json_out + "\n")
obj.free_all()
或者,您可以从list_obj
的前面以破坏性方式弹出元素,但如果必须重新分配list_obj
太多次,则可能会导致性能问题。我还没有尝试过这个,所以我不是很确定。该版本如下所示:
def write_to_file(list_obj):
with open("out.out", "w") as fout:
while len(list_obj) > 0:
obj = list_obj.pop(0)
json_out = obj.to_json()
fout.write(json_out + "\n")
obj.free_all()
您已经尝试过gc.collect()了吗?请参阅:您的test.in有多大?对于真正的解析器,输入文件大约为300Mb。@JonnyTieM,是的,我这样做了,但对性能产生了严重的负面影响。您是否尝试取消引用这些对象?请看,谢谢,但这将意味着重构我的类并混合逻辑(输出到文件并解析输入)。解析器应该能够向文件或控制台输出我需要存储解析结果的原因。能够进行iter测试或获得钥匙。最后,对于单元测试,这种方法不适合我。@AliSAIDOMAR,这很容易。只需编写一个生成器,
yield
s该值(基本上是将此答案中的代码放入def
并用yield
替换dst.write
)。然后你就可以对结果进行迭代并写入任何你想要的内容。这里的要点不是存储整个结果(以任何形式),而是逐行读取/解析/写入。按照原始程序的工作方式,内存消耗是O(文件长度),而按照您的方法(逐行读取/解析/写入),内存消耗是O(最大行长度)。谢谢。使用此代码的测试不会根据sys.getsizeof释放内存。在删除引用后是否尝试调用gc.collect()
?是的,并且处理时间已缩短