2012-04-09 6 views
6
import os 
import xml.etree.ElementTree as et 

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

ODP構造上で上記を実行すると、常にメモリが増加します。何故ですか?私はElementTreeが子ノードであるにもかかわらず解析木を構築していることを理解していますclear() ed。それがこのメモリ使用パターンの原因であれば、その周りに道がありますか?Treeをクリア()するときにElementTree.iterparse()を使用すると、メモリ使用量が増加しますか?

+0

「常に増加しています」を明確にしてください。上記をループで実行すると、メモリ使用量が爆発的に増加しますか?あるいは、すべてのオブジェクトが解放された後でさえ、これを一度実行した後に使用量が上がることを単に見ますか? – wberry

+0

私は、上記のプログラムのメモリ使用量が一定に保たれることを期待しています。代わりに、単調増加を示します。 –

+0

上記をループで実行しても、stdinを消費するだけなので、効果はありません。 –

答えて

8

各要素はclearですが、その参照はルートドキュメントに残ります。個々の要素はまだガベージコレクションできません。 ElementTreeのドキュメントのthis discussionを参照してください。

そうのようなソリューションは、ルート内の参照をクリアすることです:、VMいったんヒープストレージ用のメモリを割り当てることで、あなたの状況に影響を与えることはできませんメモリの使用状況、について覚えておくべき

# 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() 

もう一つはシステムから、一般的にそのメモリを返すことはありません。ほとんどのJava VMもこのように動作します。したがって、ヒープメモリが使用されていなくても、topまたはpsのインタープリタのサイズが減少するとは限りません。

+0

ああ、それは私が聞きたかったものです。私はETがまだ樹木を造っていると理解していましたが、それに新しくなったので、私はその樹木の根をどうやって得るのか分かりませんでした。ありがとう! –

0

同じ問題が発生しました。ドキュメンテーションは物事をはっきりさせるものではありません。私の場合の問題は:

1)子ノードのメモリを解放します。ドキュメンテーションによると、すべてのメモリが解放されています。 Clearは、そのメモリが割り当てられた親に属しているため、clearが呼び出されたメモリを解放しません。 2)ルートが何であるかに依存するroot.clear()を呼び出します。もしrootが親ならばそれはうまくいくでしょう。それ以外の場合は、メモリを解放しません。

修正は、親への参照を保持することでした。ノードが不要になったときは、parent.remove(child_node)を呼び出します。これは機能し、メモリプロファイルは数KBに抑えられました。

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 

関心の要素はサブ要素を持っていません。終了タグが表示されるとすぐに削除されます。必要なのは要素のテキストまたは属性だけであればOKです。

要素の子孫に問い合わせる場合は、子孫の完全な分岐を作成する必要があります。このために、これらの要素の深さカウンタとして実装されたフラグを維持します。深さがゼロの場合は.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 
関連する問題