import os
import xml.etree.ElementTree as et
for ev, el in et.iterparse(os.sys.stdin):
el.clear()
在ODP结构RDF dump上运行上述内容会导致内存不断增加。这是为什么?我理解ElementTree仍然构建一个解析树,尽管有子节点clear()
编辑。如果这是这种内存使用模式的原因,有没有办法呢?
答案 0 :(得分:8)
您clear
每个元素,但对它们的引用仍保留在根文档中。所以单个元素仍然不能被垃圾收集。请参阅ElementTree文档中的this discussion。
解决方案是清除根目录中的引用,如下所示:
# get an iterable
context = iterparse(source, events=("start", "end"))
# turn it into an iterator
context = iter(context)
# get the root element
event, root = context.next()
for event, elem in context:
if event == "end" and elem.tag == "record":
... process record elements ...
root.clear()
另一件需要记住的内存使用情况可能不会影响您的情况,一旦VM从系统中为堆存储分配内存,它通常永远不会返回内存。大多数Java VM也以这种方式工作。所以你不应该期望top
或ps
中解释器的大小会减少,即使堆内存未使用也是如此。
答案 1 :(得分:1)
正如Kevin Guerra的回答中提到的," root.clear()" ElementTree文档中的策略仅删除根的完全解析的子项。如果那些孩子正在锚定巨大的树枝,那就没那么有用了。
他谈到了理想的解决方案,但没有发布任何代码,所以这里有一个例子:
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
答案 2 :(得分:0)
我遇到了同样的问题。文档没有说清楚。我的问题是:
1)调用clear会释放子节点的内存。文档说它释放所有内存。 Clear不会释放调用clear的内存,因为该内存属于分配它的父级。 2)调用root.clear(),这取决于root是什么。如果root是父级,那么它将起作用。否则,它不会释放内存。
修复是保留对父项的引用,当我们不再需要该节点时,我们调用parent.remove(child_node)。这很有效,它将内存配置文件保持在几KB。