6

XPath を使用して HTML を分析するコマンド ライン php スクレイピング アプリを構築しました。問題は、新しい DOMXPath クラス インスタンスがループで読み込まれるたびに、読み込まれる XML のサイズとほぼ同じメモリ損失が発生することです。 . スクリプトが実行され、実行され、制限に達して終了するまでメモリ使用量がゆっくりと増加します。

でガベージ コレクションを強制しようとしましたがgc_collect_cycles()、PHP はまだ古い Xpath 要求からメモリを取得していません。実際、DOMXPath クラスの定義には、デストラクタ関数さえ含まれていないように見えますか?

So my question is ... is there any way to force garbage clean up on DOMXPath after I've already extracted the necessary data? Using unset on the class instance predictably does nothing.

The code is nothing special, just standard Xpath stuff:

//Loaded outside of loop
$this->dom = new DOMDocument(); 

//Inside Loop
$this->dom->loadHTML($output);  
$xpath = new DOMXPath($this->dom);
$nodes = $xpath->query("//span[@class='ckass']");

//unset($this->dom) and unset($xpath) doesn't seem to have any effect

As you can see above I've kept the instantiation of a new DOMDocument class outside of the loop, although that doesn't seem to improve performance. I've even tried taking the $xpath class instance out of the loop and loading the DOM into Xpath directly using the __constructor method, memory loss is the same.

4

2 に答える 2

3

この答えを見た後、彼女は何年も結論を出していませんでしたが、ついに更新されました! 私は今、同様の問題に遭遇しましたDOMXPathが、メモリがリークするだけで、制御できないことがわかりました。これが bug.php.net でこれまでに報告されているかどうかは調べていません (これは後で編集するのに役立つ可能性があります)。

問題に対して私が見つけた「機能する」解決策は、単なる回避策です。DOMNodeList Traversable基本的な考え方は、返された byDOMXPath::query()を同じノードを含む別のものに置き換えることでした。

最も適切な回避DOMXPathElementsIterator策は、メモリリークなしで質問にある具体的な xpath 式をクエリできるようにすることです。

$nodes = new DOMXPathElementsIterator($this->dom, "//span[@class='ckass']");

foreach ($nodes as $span) {
   ...
}

このクラスは現在、Iterator-Garden の開発バージョンの一部であり、$nodesすべての<span>DOMElements に対する反復子です。

この回避策の欠点は、メモリ リークを防ぐために内部で使用されるため、xpath の結果が結果に限定されるSimpleXMLElement::xpath()ことです (これは とは異なります)。DOMXPath::query()

もう 1 つの方法は、によって返されるのと同じように を使用DOMNodeListIteratorすることです。ただし、これらの反復は遅いです。DOMNodeListDOMDocument::getElementsByTagname()

質問が本当に古かったとしても、これが役に立つことを願っています。似たような状況で助かりました。


ガベージ コレクション クリーンアップ サークルの呼び出しは、オブジェクトが参照 (使用) されなくなった場合にのみ意味があります。

たとえばDOMXPath、同じものに対して新しいオブジェクトを何DOMDocument度も作成すると (まだ存在する に接続されていることに注意しDOMDocumentてください)、メモリが「リーク」しているように聞こえます。ますます多くのメモリを使用するだけです。

代わりに、常にオブジェクトを再利用するので、既存のDOMXPathオブジェクトを再利用できますDOMDocument。試してみる:

//Loaded outside of loop
$this->dom = new DOMDocument(); 
$xpath = new DOMXPath($this->dom);

//Inside Loop
$this->dom->loadHTML($output);  
$nodes = $xpath->query("//span[@class='ckass']");
于 2011-11-18T20:57:43.777 に答える