import os
import xml.etree.ElementTree as et
for ev, el in et.iterparse(os.sys.stdin):
el.clear()
在 ODP 结构RDF 转储上运行上述内容总是会增加内存。这是为什么?我知道 ElementTree 仍然构建一个解析树,尽管有子节点clear()
编辑。如果这是这种内存使用模式的原因,有没有办法解决它?
import os
import xml.etree.ElementTree as et
for ev, el in et.iterparse(os.sys.stdin):
el.clear()
在 ODP 结构RDF 转储上运行上述内容总是会增加内存。这是为什么?我知道 ElementTree 仍然构建一个解析树,尽管有子节点clear()
编辑。如果这是这种内存使用模式的原因,有没有办法解决它?
您正在clear
读取每个元素,但对它们的引用仍保留在根文档中。所以单个元素仍然不能被垃圾收集。
解决方案是清除根中的引用,如下所示:
import xml.etree.ElementTree as ET
# get iterator
context = ET.iterparse(source, events=("start", "end"))
# get the root element
event, root = next(context)
for event, elem in context:
if event == "end" and elem.tag == "record":
# process record elements here...
root.clear()
关于内存使用的另一件事要记住,这可能不会影响您的情况,即一旦 VM 从系统为堆存储分配内存,它通常永远不会归还该内存。大多数 Java VM 也以这种方式工作。因此,即使该堆内存未被使用,您也不应该期望解释器的大小会减少top
或减少。ps
为了在 Python 3+ 中工作而更改了代码。
正如 Kevin Guerra 的回答中提到的,ElementTree 文档中的“root.clear()”策略只删除了根的完全解析的子节点。如果那些孩子正在锚定巨大的树枝,那不是很有帮助。
他谈到了理想的解决方案,但没有发布任何代码,所以这里是一个例子:
element_stack = []
context = ET.iterparse(stream, events=('start', 'end'))
for event, elem in context:
if event == 'start':
element_stack.append(elem)
elif event == 'end':
element_stack.pop()
# see if elem is one of interest and do something with it here
if element_stack:
element_stack[-1].remove(elem)
del context
感兴趣的元素将没有子元素;一旦看到它们的结束标签,它们就会被删除。如果您只需要元素的文本或属性,这可能没问题。
如果要查询元素的后代,则需要为其构建完整的分支。为此,维护一个标志,实现为这些元素的深度计数器。仅在深度为零时调用 .remove() :
element_stack = []
interesting_element_depth = 0
context = ET.iterparse(stream, events=('start', 'end'))
for event, elem in context:
if event == 'start':
element_stack.append(elem)
if elem.tag == 'foo':
interesting_element_depth += 1
elif event == 'end':
element_stack.pop()
if elem.tag == 'foo':
interesting_element_depth -= 1
# do something with elem and its descendants here
if element_stack and not interesting_element_depth:
element_stack[-1].remove(elem)
del context
我遇到了同样的问题。该文档并没有使事情变得非常清楚。我的问题是:
1) 调用 clear 确实会释放子节点的内存。文档说它会释放所有内存。Clear 不会释放调用 clear 的内存,因为该内存属于分配它的父级。2) 调用 root.clear(),这取决于 root 是什么。如果 root 是父级,那么它将起作用。否则,它不会释放内存。
修复方法是保留对父节点的引用,当我们不再需要该节点时,我们调用 parent.remove(child_node)。这很有效,它使内存配置文件保持在几 KB。