PHP简单的HTML Dom解析器内存泄漏/使用

时间:2013-08-06 20:49:22

标签: php domparser

我正在尝试使用PHP Simple HTML Dom Parser来解析某些网站的一些信息。无所谓何在何处。但似乎它存在一些巨大的内存问题。我设法将html代码削减到只有6kB,但是找到一些元素并将它们保存到数据库的脚本甚至需要700MB的内存和超过1GB的虚拟内存!我读到了应该使用的地方 - > clear()来释放一些内存,但似乎并非如此。

我使用str_get_html()一次,使用->find()使用$main_html = str_get_html($main_site); $x = $main_html->find(...); $y = $main_html->find(...); etc. 将结果分配给变量。

$y->clear()

我在使用$ y之后尝试使用PHP Fatal error: Call to a member function clear() on a non-object,但即使$y确实存在且if($y)为真,我也会收到错误foreach($y) echo $y->plaintext。即使plaintext确实会返回$y的{​​{1}}。

来自htop:

PID USER     PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
8839 username    20   0 1068M  638M   268 R 23.0  8.0  0:08.41 php myscript.php

有什么问题?

简单测试:

echo "(MEM:".memory_get_usage()."->";
$product = $p->find('a',0)->href;
echo memory_get_usage()."->";
unset($product);
$p->clear();
unset($p);
echo memory_get_usage().")";

结果是:

(MEM:11865648->11866192->11865936)

更易读的形式:

11865648->
11866192-> (+544 in total)
11865936 (+288 in total)

当然我不能使用$ product-> clear(),因为它说PHP Fatal error: Call to a member function clear() on a non-object

2 个答案:

答案 0 :(得分:9)

使用str_html_get或类似功能创建simple_html_dom对象几次而不清除和破坏前一个对象时,似乎存在一些内存问题。特别是在使用 - > find时会创建simple_html_dom_node个对象的数组。甚至作者网站上的常见问题解答也说明在创建新对象之前清除并销毁之前的simple_html_dom对象,但有时如果没有额外的代码和内存则无法完成。

这就是为什么我创建了这个函数,从内存中删除所有PHP Simple HTML Dom Parser跟踪:

function clean_all(&$items,$leave = ''){
    foreach($items as $id => $item){
        if($leave && ((!is_array($leave) && $id == $leave) || (is_array($leave) && in_array($id,$leave)))) continue;
        if($id != 'GLOBALS'){
            if(is_object($item) && ((get_class($item) == 'simple_html_dom') || (get_class($item) == 'simple_html_dom_node'))){
                $items[$id]->clear();
                unset($items[$id]);
            }else if(is_array($item)){
                $first = array_shift($item);
                if(is_object($first) && ((get_class($first) == 'simple_html_dom') || (get_class($first) == 'simple_html_dom_node'))){
                    unset($items[$id]);
                }
                unset($first);
            }
        }
    }
}

用法:

从内存中清除PHP Simple HTML Dom Parser的所有痕迹:clean_all($GLOBALS);

从内存中清除PHP Simple HTML Dom Parser的所有痕迹,除了$ myobj:clean_all($GLOBALS,'myobj');

从内存中清除PHP Simple HTML Dom Parser的所有痕迹,除了对象列表($ myobj1,$ myobj2 ...):clean_all($GLOBALS,array('myobj1','myobj2'));

希望它也能帮助别人。


通常我在使用str_to_html()两次时使用它,如:

$site=file_get_contents('http://google.com');
$site_html=str_get_html($site);
foreach($site->find('a') as $a){
   $site2=file_get_contents($a->href);
   $site2_html=str_get_html($site2);
   echo $site2->find('p',0)->plaintext;
}
clean_all($_GLOBALS);

在此示例中,我$site_html->clear()之前无法foreach{},因为foreach将失败。并且因为调用多个str_get_html()而不清除以前的那些,所以冗余的依赖关系正在被破坏并且在所有事件都留下内存泄漏之后将其清除。这就是为什么我的函数必须搜索simple_html_dom对象的已定义变量并手动清除它们。

在我的情况下,我在foreach内部分叉,经过几步后,主要的PHP脚本使用像100MB的内存。分叉几次,它一直在增加和增加,最后杀死我的服务器。好吧差不多。当然,当PHP脚本结束时,它会释放内存。但是当使用8GB内存时,花了好几年才结束。

答案 1 :(得分:2)

我认为您需要在clear()

上致电$main_html

From the docs ...

问:这个脚本严重泄漏了内存......运行完毕后,它没有正确地从内存中清除dom对象..

答:由于php5循环引用内存泄漏,在创建DOM对象后,如果调用file_get_dom()多一次,则必须调用$ dom-> clear()来释放内存。

示例:

$html = file_get_html(...); 
// do something... 
$html->clear(); 
unset($html);