清除()树时使用ElementTree.iterparse()时内存使用量是否会增加?

时间:2012-04-09 13:49:56

标签: python memory-leaks elementtree

import os
import xml.etree.ElementTree as et

for ev, el in et.iterparse(os.sys.stdin):
    el.clear()

在ODP结构RDF dump上运行上述内容会导致内存不断增加。这是为什么?我理解ElementTree仍然构建一个解析树,尽管有子节点clear()编辑。如果这是这种内存使用模式的原因,有没有办法呢?

3 个答案:

答案 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也以这种方式工作。所以你不应该期望topps中解释器的大小会减少,即使堆内存未使用也是如此。

答案 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。