DOMDocument / Xpath утечка памяти при длительном процессе командной строки – любой способ деконструировать этот класс

Я создаю приложение php scraping для командной строки, которое использует XPath для анализа HTML – проблема возникает каждый раз, когда новый экземпляр класса DOMXPath загружается в цикле. Я получаю потерю памяти, примерно равную размеру загружаемого XML , Скрипт запускается и запускается, медленно наращивая объем памяти до тех пор, пока он не достигнет предела и не прекратит работу.

Я пробовал форсировать сбор мусора с помощью gc_collect_cycles() и PHP по-прежнему не возвращает память из старых запросов Xpath. Действительно, определение класса DOMXPath, похоже, даже не включает функцию деструктора?

Поэтому мой вопрос … есть ли способ принудительно удалить мусор на DOMXPath после того, как я уже извлек необходимые данные? Использование unset в экземпляре класса предсказуемо ничего не делает.

Код ничего особенного, просто стандартный материал Xpath:

 //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 

Как вы можете видеть выше, я сохранил экземпляр нового класса DOMDocument за пределами цикла, хотя, похоже, это не улучшает производительность. Я даже попытался вывести экземпляр класса $xpath из цикла и загрузить DOM в Xpath напрямую, используя метод __constructor , потеря памяти одинаков.

Solutions Collecting From Web of "DOMDocument / Xpath утечка памяти при длительном процессе командной строки – любой способ деконструировать этот класс"

Увидев этот ответ, она в течение многих лет без заключения, наконец, обновление! Теперь я столкнулся с аналогичной проблемой, и оказалось, что DOMXPath просто просачивает память, и вы не можете ее контролировать. Я не искал, если об этом сообщается на bug.php.net до сих пор (это может быть полезно для редактирования позже).

«Рабочие» решения, которые я нашел в этой проблеме, – это обходные пути. Основная идея заключалась в замене DOMNodeList Traversable возвращаемого DOMXPath::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() ), потому что он используется внутренне для предотвращения утечки памяти.

Другой альтернативой является использование DOMNodeListIterator над DOMNodeList например, возвращаемый DOMDocument::getElementsByTagname() . Однако эти итерации медленны.

Надеюсь, что это будет полезно, даже вопрос был действительно старым. Это помогло мне в аналогичной ситуации.


Вызов групп очистки мусора делает смысл, если объекты больше не ссылаются (используются).

Например, если вы DOMXPath создаете новый объект 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']"); 

Если вы используете libxml_use_internal_errors(true); чем причина утечки памяти, потому что список ошибок растет.

Использовать libxml_clear_errors(); или проверьте этот ответ .