我正在使用lxml.etree
从一堆XML文件加载数据,但是在我完成此初始解析后,我想关闭它们。目前,以下代码中的XML_FILES
列表占用了程序的400 MiB的已用内存400 MiB。我已经尝试了del XML_FILES
,del XML_FILES[:]
,XML_FILES = None
,for etree in XML_FILES: etree = None
等等,但这些似乎都没有奏效。我也无法在lxml文档中找到关闭lxml文件的任何内容。这是进行解析的代码:
def open_xml_files():
return [etree.parse(filename) for filename in paths]
def load_location_data(xml_files):
location_data = {}
for xml_file in xml_files:
for city in xml_file.findall('City'):
code = city.findtext('CityCode')
name = city.findtext('CityName')
location_data['city'][code] = name
# [A few more like the one above]
return location_data
XML_FILES = utils.open_xml_files()
LOCATION_DATA = load_location_data(XML_FILES)
# XML_FILES never used again from this point on
现在,我如何在这里摆脱XML_FILES?
答案 0 :(得分:4)
您可能会考虑etree.iterparse
,它使用生成器而不是内存列表。结合生成器表达式,这可能会为程序节省一些内存。
def open_xml_files():
return (etree.iterparse(filename) for filename in paths)
iterparse
在解析的文件内容上创建生成器,而parse
立即解析文件并将内容加载到内存中。内存使用的差异来自于iterparse
在调用next()
方法之前实际上没有做任何事情(在这种情况下,通过for
循环隐式)。
编辑:显然iterparse会以增量方式工作,但不会释放内存,就像解析一样。您可以使用this answer中的解决方案在遍历xml文档时释放内存。
答案 1 :(得分:3)
鉴于内存使用率在第二次解析文件时不会翻倍,如果在解析之间删除了结构(请参阅注释),那么这里发生了什么:
malloc
。malloc
想要内存,所以请求操作系统。del
删除结构。但是,malloc
的对应free
实际上并没有将内存返回给操作系统。相反,它坚持服务于未来的请求。malloc
从之前从操作系统获得的相同区域提供内存。这是malloc
实现的典型行为。 memory_profiler
仅检查进程的总内存,包括malloc
保留用于重用的部分。对于使用大的,连续的内存块(例如大型NumPy阵列)的应用程序,这很好,因为它们实际上已返回到操作系统。(*)但对于像LXML这样请求大量较小分配的库,{{1}将给出一个上限,而不是一个确切的数字。
(*)至少在Linux上使用Glibc。我不确定MacOS和Windows是做什么的。
答案 2 :(得分:1)
如何将占用内存的代码作为一个单独的进程运行,并将内存释放到操作系统?在你的情况下,这应该做的工作:
from multiprocessing import Process, Queue
def get_location_data(q):
XML_FILES = utils.open_xml_files()
q.put(load_location_data(XML_FILES))
q = Queue()
p = Process(target=get_location_data, args=((q,)))
p.start()
result = q.get() # your location data
if p.is_alive():
p.terminate()
答案 3 :(得分:0)
我发现的其他解决方案效率很低,但这对我有用:
def destroy_tree(tree):
root = tree.getroot()
node_tracker = {root: [0, None]}
for node in root.iterdescendants():
parent = node.getparent()
node_tracker[node] = [node_tracker[parent][0] + 1, parent]
node_tracker = sorted([(depth, parent, child) for child, (depth, parent)
in node_tracker.items()], key=lambda x: x[0], reverse=True)
for _, parent, child in node_tracker:
if parent is None:
break
parent.remove(child)
del tree