8
import os
import xml.etree.ElementTree as et

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

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

4

3 回答 3

10

您正在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+ 中工作而更改了代码。

于 2012-04-09T19:26:35.503 回答
1

正如 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
于 2017-06-12T22:26:27.410 回答
0

我遇到了同样的问题。该文档并没有使事情变得非常清楚。我的问题是:

1) 调用 clear 确实会释放子节点的内存。文档说它会释放所有内存。Clear 不会释放调用 clear 的内存,因为该内存属于分配它的父级。2) 调用 root.clear(),这取决于 root 是什么。如果 root 是父级,那么它将起作用。否则,它不会释放内存。

修复方法是保留对父节点的引用,当我们不再需要该节点时,我们调用 parent.remove(child_node)。这很有效,它使内存配置文件保持在几 KB。

于 2016-03-21T22:52:31.127 回答